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