1---
2name: writing-rust
3description: Writes idiomatic, well-structured Rust code following community conventions and best practices. Use when working on Rust projects, writing Rust code, debugging Rust errors, or when the user mentions Rust, Cargo, or crates.
4user-invocable: true
5license: LicenseRef-MutuaL-1.2
6metadata:
7 author: Amolith <amolith@secluded.site>
8---
9
10Write idiomatic Rust. All code must compile, pass clippy, and have tests.
11
12## Code quality checks
13
14Run these in order before considering any code complete. Never submit code that fails any check.
15
16```bash
17cargo fmt
18cargo check
19cargo clippy -- -D warnings
20cargo test
21```
22
23## Project structure
24
25```
26project-root/
27├── Cargo.toml
28├── Cargo.lock
29├── src/
30│ ├── main.rs # Binary entry point (if applicable)
31│ ├── lib.rs # Library root (if applicable)
32│ └── module_name/
33│ ├── mod.rs
34│ └── submodule.rs
35├── tests/ # Integration tests
36├── benches/ # Benchmarks (if applicable)
37└── examples/ # Example usage (if applicable)
38```
39
40## Naming conventions
41
42| Item | Convention | Example |
43| --------------- | ----------------- | --------------------- |
44| Crates | `snake_case` | `my_crate` |
45| Modules | `snake_case` | `my_module` |
46| Types / Traits | `PascalCase` | `MyStruct`, `MyTrait` |
47| Functions | `snake_case` | `do_something()` |
48| Constants | `SCREAMING_SNAKE` | `MAX_RETRIES` |
49| Local variables | `snake_case` | `item_count` |
50| Type parameters | Single uppercase | `T`, `E`, `K`, `V` |
51| Lifetimes | Short lowercase | `'a`, `'de` |
52
53## Error handling
54
55- Prefer `Result<T, E>` over `panic!()` for recoverable errors.
56- Use `thiserror` for library error types, `anyhow` for application-level errors.
57- Avoid `.unwrap()` and `.expect()` in production code. Propagate with `?`.
58- Define custom error types when a module has more than one failure mode.
59- When a fallible function consumes an argument, return it inside the error type so callers can recover it.
60
61## Ownership and borrowing
62
63- Prefer borrowing (`&T`, `&mut T`) over transferring ownership when the caller doesn't need to give up the value.
64- Use `Clone` sparingly — never just to satisfy the borrow checker.
65- Prefer `&str` over `String`, `&[T]` over `&Vec<T>`, and `&T` over `&Box<T>` in function parameters.
66- Use `Cow<'_, str>` when a function may or may not need to allocate.
67- Use `mem::take` or `mem::replace` to move values out of mutable references without cloning.
68
69## Structs and enums
70
71- Derive common traits where appropriate: `Debug`, `Clone`, `PartialEq`, `Eq`, `Hash`, `Default`.
72- Use the builder pattern for structs with many optional fields.
73- Prefer enums over boolean flags for state representation.
74- Use `#[non_exhaustive]` on public structs and enums that may grow over time.
75- Use the newtype pattern for type safety wrappers around primitives (zero-cost abstraction).
76- Decompose large structs into smaller ones when the borrow checker prevents independent field access.
77
78## Testing
79
80- All implemented functionality must have corresponding tests.
81- Tests must validate behavior, not just assert `true`.
82- Use descriptive test names that explain what is being tested.
83- Test both the happy path and error/edge cases.
84
85```bash
86cargo test # Run all tests
87cargo test test_name # Run a specific test
88cargo test -- --nocapture # Run tests with output
89```
90
91## Design principles
92
93- **KISS**: Simplicity should be a key goal in design.
94- **YAGNI**: Don't add functionality until it is necessary.
95- **DRY**: Every piece of knowledge should have a single, authoritative representation.
96- **SOLID**: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion.
97- **Composition over inheritance**: Favour traits and composition over emulating inheritance.
98- **Command-Query Separation**: Functions should either return data or produce side effects, not both.
99- **Principle of Least Astonishment**: Components should behave the way most users expect.
100
101## Performance
102
103- Avoid unnecessary heap allocations. Prefer stack allocation and slices.
104- Use `&[T]` instead of `&Vec<T>` and `&str` instead of `&String` in function signatures.
105- Profile before optimizing. Use `cargo bench` and tools like `criterion`.
106- Stay lazy with iterators. Avoid `.collect()`-ing unnecessarily.
107
108## Dependencies
109
110- Prefer well-maintained, widely-used crates. Pin versions in `Cargo.toml`.
111- Minimize the dependency tree. Avoid adding crates for trivial functionality.
112- Use `cargo audit` to check for known vulnerabilities.
113
114## Documentation
115
116- All public items (`pub`) must have doc comments.
117- Include examples in doc comments for public functions.
118- Use module-level documentation at the top of files.
119
120## Anti-patterns
121
122Common but counterproductive solutions. **Avoid them.**
123
124### Clone to satisfy the borrow checker
125
126Do not resolve borrow checker errors by cloning without understanding the consequences. Cloning creates independent copies — changes to one are not reflected in the other.
127
128If the borrow checker complains, first understand the ownership issue. Use `mem::take`, restructure borrows, or redesign the data flow.
129
130**Exception:** `Rc` and `Arc` are designed for shared ownership via clone. Cloning them is cheap and correct. Deliberate cloning is also fine when ownership semantics require it, or for prototypes and non-performance-critical code.
131
132### `#![deny(warnings)]`
133
134Do not use `#![deny(warnings)]` in crate roots. New compiler versions may introduce new warnings, breaking builds unexpectedly.
135
136Instead, deny specific named lints explicitly, or use `RUSTFLAGS="-D warnings"` in CI. This preserves Rust's stability guarantees while still enforcing lint discipline.
137
138### Deref polymorphism
139
140Do not misuse the `Deref` trait to emulate struct inheritance. `Deref` is designed for smart pointers (`pointer-to-T` → `T`), not for converting between arbitrary types.
141
142It does not introduce subtyping. Traits on the inner type are not automatically available on the outer type. It interacts badly with generics and bounds checking.
143
144Instead, use composition with explicit delegation methods, or use traits for shared behaviour.
145
146## Further reference
147
148Before implementing any non-trivial pattern — builders, visitors, newtype wrappers, RAII guards, command objects, struct decomposition for borrow splitting, etc. — read [idioms.md](references/idioms.md). It covers the community-agreed idioms and design patterns for Rust, and getting these right the first time avoids painful refactors later.