1# Rust coding guidelines
2
3* Prioritize code correctness and clarity. Speed and efficiency are secondary priorities unless otherwise specified.
4* Do not write organizational or comments that summarize the code. Comments should only be written in order to explain "why" the code is written in some way in the case there is a reason that is tricky / non-obvious.
5* Prefer implementing functionality in existing files unless it is a new logical component. Avoid creating many small files.
6* Avoid using functions that panic like `unwrap()`, instead use mechanisms like `?` to propagate errors.
7* Be careful with operations like indexing which may panic if the indexes are out of bounds.
8* Never silently discard errors with `let _ =` on fallible operations. Always handle errors appropriately:
9 - Propagate errors with `?` when the calling function should handle them
10 - Use `.log_err()` or similar when you need to ignore errors but want visibility
11 - Use explicit error handling with `match` or `if let Err(...)` when you need custom logic
12 - Example: avoid `let _ = client.request(...).await?;` - use `client.request(...).await?;` instead
13* When implementing async operations that may fail, ensure errors propagate to the UI layer so users get meaningful feedback.
14* Never create files with `mod.rs` paths - prefer `src/some_module.rs` instead of `src/some_module/mod.rs`.
15* When creating new crates, prefer specifying the library root path in `Cargo.toml` using `[lib] path = "...rs"` instead of the default `lib.rs`, to maintain consistent and descriptive naming (e.g., `gpui.rs` or `main.rs`).
16* Avoid creative additions unless explicitly requested
17* Use full words for variable names (no abbreviations like "q" for "queue")
18* Use variable shadowing to scope clones in async contexts for clarity, minimizing the lifetime of borrowed references.
19 Example:
20 ```rust
21 executor.spawn({
22 let task_ran = task_ran.clone();
23 async move {
24 *task_ran.borrow_mut() = true;
25 }
26 });
27 ```
28
29# Timers in tests
30
31* In GPUI tests, prefer GPUI executor timers over `smol::Timer::after(...)` when you need timeouts, delays, or to drive `run_until_parked()`:
32 - Use `cx.background_executor().timer(duration).await` (or `cx.background_executor.timer(duration).await` in `TestAppContext`) so the work is scheduled on GPUI's dispatcher.
33 - Avoid `smol::Timer::after(...)` for test timeouts when you rely on `run_until_parked()`, because it may not be tracked by GPUI's scheduler and can lead to "nothing left to run" when pumping.
34
35# GPUI
36
37GPUI is a UI framework which also provides primitives for state and concurrency management.
38
39## Context
40
41Context types allow interaction with global state, windows, entities, and system services. They are typically passed to functions as the argument named `cx`. When a function takes callbacks they come after the `cx` parameter.
42
43* `App` is the root context type, providing access to global state and read and update of entities.
44* `Context<T>` is provided when updating an `Entity<T>`. This context dereferences into `App`, so functions which take `&App` can also take `&Context<T>`.
45* `AsyncApp` and `AsyncWindowContext` are provided by `cx.spawn` and `cx.spawn_in`. These can be held across await points.
46
47## `Window`
48
49`Window` provides access to the state of an application window. It is passed to functions as an argument named `window` and comes before `cx` when present. It is used for managing focus, dispatching actions, directly drawing, getting user input state, etc.
50
51## Entities
52
53An `Entity<T>` is a handle to state of type `T`. With `thing: Entity<T>`:
54
55* `thing.entity_id()` returns `EntityId`
56* `thing.downgrade()` returns `WeakEntity<T>`
57* `thing.read(cx: &App)` returns `&T`.
58* `thing.read_with(cx, |thing: &T, cx: &App| ...)` returns the closure's return value.
59* `thing.update(cx, |thing: &mut T, cx: &mut Context<T>| ...)` allows the closure to mutate the state, and provides a `Context<T>` for interacting with the entity. It returns the closure's return value.
60* `thing.update_in(cx, |thing: &mut T, window: &mut Window, cx: &mut Context<T>| ...)` takes a `AsyncWindowContext` or `VisualTestContext`. It's the same as `update` while also providing the `Window`.
61
62Within the closures, the inner `cx` provided to the closure must be used instead of the outer `cx` to avoid issues with multiple borrows.
63
64Trying to update an entity while it's already being updated must be avoided as this will cause a panic.
65
66`WeakEntity<T>` is a weak handle. It has `read_with`, `update`, and `update_in` methods that work the same, but always return an `anyhow::Result` so that they can fail if the entity no longer exists. This can be useful to avoid memory leaks - if entities have mutually recursive handles to each other they will never be dropped.
67
68## Concurrency
69
70All use of entities and UI rendering occurs on a single foreground thread.
71
72`cx.spawn(async move |cx| ...)` runs an async closure on the foreground thread. Within the closure, `cx` is `&mut AsyncApp`.
73
74When the outer cx is a `Context<T>`, the use of `spawn` instead looks like `cx.spawn(async move |this, cx| ...)`, where `this: WeakEntity<T>` and `cx: &mut AsyncApp`.
75
76To do work on other threads, `cx.background_spawn(async move { ... })` is used. Often this background task is awaited on by a foreground task which uses the results to update state.
77
78Both `cx.spawn` and `cx.background_spawn` return a `Task<R>`, which is a future that can be awaited upon. If this task is dropped, then its work is cancelled. To prevent this one of the following must be done:
79
80* Awaiting the task in some other async context.
81* Detaching the task via `task.detach()` or `task.detach_and_log_err(cx)`, allowing it to run indefinitely.
82* Storing the task in a field, if the work should be halted when the struct is dropped.
83
84A task which doesn't do anything but provide a value can be created with `Task::ready(value)`.
85
86## Elements
87
88The `Render` trait is used to render some state into an element tree that is laid out using flexbox layout. An `Entity<T>` where `T` implements `Render` is sometimes called a "view".
89
90Example:
91
92```
93struct TextWithBorder(SharedString);
94
95impl Render for TextWithBorder {
96 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
97 div().border_1().child(self.0.clone())
98 }
99}
100```
101
102Since `impl IntoElement for SharedString` exists, it can be used as an argument to `child`. `SharedString` is used to avoid copying strings, and is either an `&'static str` or `Arc<str>`.
103
104UI components that are constructed just to be turned into elements can instead implement the `RenderOnce` trait, which is similar to `Render`, but its `render` method takes ownership of `self` and receives `&mut App` instead of `&mut Context<Self>`. Types that implement this trait can use `#[derive(IntoElement)]` to use them directly as children.
105
106The style methods on elements are similar to those used by Tailwind CSS.
107
108If some attributes or children of an element tree are conditional, `.when(condition, |this| ...)` can be used to run the closure only when `condition` is true. Similarly, `.when_some(option, |this, value| ...)` runs the closure when the `Option` has a value.
109
110## Input events
111
112Input event handlers can be registered on an element via methods like `.on_click(|event, window, cx: &mut App| ...)`.
113
114Often event handlers will want to update the entity that's in the current `Context<T>`. The `cx.listener` method provides this - its use looks like `.on_click(cx.listener(|this: &mut T, event, window, cx: &mut Context<T>| ...)`.
115
116## Actions
117
118Actions are dispatched via user keyboard interaction or in code via `window.dispatch_action(SomeAction.boxed_clone(), cx)` or `focus_handle.dispatch_action(&SomeAction, window, cx)`.
119
120Actions with no data defined with the `actions!(some_namespace, [SomeAction, AnotherAction])` macro call. Otherwise the `Action` derive macro is used. Doc comments on actions are displayed to the user.
121
122Action handlers can be registered on an element via the event handler `.on_action(|action, window, cx| ...)`. Like other event handlers, this is often used with `cx.listener`.
123
124## Notify
125
126When a view's state has changed in a way that may affect its rendering, it should call `cx.notify()`. This will cause the view to be rerendered. It will also cause any observe callbacks registered for the entity with `cx.observe` to be called.
127
128## Entity events
129
130While updating an entity (`cx: Context<T>`), it can emit an event using `cx.emit(event)`. Entities register which events they can emit by declaring `impl EventEmitter<EventType> for EntityType {}`.
131
132Other entities can then register a callback to handle these events by doing `cx.subscribe(other_entity, |this, other_entity, event, cx| ...)`. This will return a `Subscription` which deregisters the callback when dropped. Typically `cx.subscribe` happens when creating a new entity and the subscriptions are stored in a `_subscriptions: Vec<Subscription>` field.
133
134## Build guidelines
135
136- Use `./script/clippy` instead of `cargo clippy`
137
138# Pull request hygiene
139
140When an agent opens or updates a pull request, it must:
141
142- Use a clear, correctly capitalized, imperative PR title (for example, `Fix crash in project panel`).
143- Avoid conventional commit prefixes in PR titles (`fix:`, `feat:`, `docs:`, etc.).
144- Avoid trailing punctuation in PR titles.
145- Optionally prefix the title with a crate name when one crate is the clear scope (for example, `git_ui: Add history view`).
146- Include a `Release Notes:` section as the final section in the PR body.
147- Use one bullet under `Release Notes:`:
148 - `- Added ...`, `- Fixed ...`, or `- Improved ...` for user-facing changes, or
149 - `- N/A` for docs-only and other non-user-facing changes.
150- Format release notes exactly with a blank line after the heading, for example:
151
152```
153Release Notes:
154
155- N/A
156```
157
158# Crash Investigation
159
160## Sentry Integration
161- Crash investigation prompts: `.factory/prompts/crash/investigate.md`
162- Crash fix prompts: `.factory/prompts/crash/fix.md`
163- Fetch crash reports: `script/sentry-fetch <issue-id>`
164- Generate investigation prompt from crash: `script/crash-to-prompt <issue-id>`
165
166# Rules Hygiene
167
168These `.rules` files are read by every agent session. Keep them high-signal.
169
170## After any agentic session
171If you discover a non-obvious pattern that would help future sessions, include a **"Suggested .rules additions"** heading in your PR description with the proposed text. Do **not** edit `.rules` inline during normal feature/fix work. Reviewers decide what gets merged.
172
173## High bar for new rules
174Editing or clarifying existing rules is always welcome. New rules must meet **all three** criteria:
1751. **Non-obvious** — someone familiar with the codebase would still get it wrong without the rule.
1762. **Repeatedly encountered** — it came up more than once (multiple hits in one session counts).
1773. **Specific enough to act on** — a concrete instruction, not a vague principle.
178
179Rules that apply to a single crate belong in that crate's own `.rules` file, not the repo root.
180
181## What NOT to put in `.rules`
182Avoid architectural descriptions of a crate (module layout, data flow, key types). These go stale fast and the agent can gather them by reading the code. Rules should be **traps to avoid**, not **maps to follow**.
183
184## No drive-by additions
185Rules emerge from validated patterns, not one-off observations. The workflow is:
1861. Agent notes a pattern during a session.
1872. Team validates the pattern in code review.
1883. A dedicated commit adds the rule with context on *why* it exists.