---
name: writing-rust
description: 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.
license: GPL-3.0-or-later
metadata:
  author: Amolith <amolith@secluded.site>
---

Write idiomatic Rust. All code must compile, pass clippy, and have tests.

## Code quality checks

Run these in order before considering any code complete. Never submit code that fails any check.

```bash
cargo fmt
cargo check
cargo clippy -- -D warnings
cargo test
```

## Project structure

```
project-root/
├── Cargo.toml
├── Cargo.lock
├── src/
│   ├── main.rs              # Binary entry point (if applicable)
│   ├── lib.rs               # Library root (if applicable)
│   └── module_name/
│       ├── mod.rs
│       └── submodule.rs
├── tests/                   # Integration tests
├── benches/                 # Benchmarks (if applicable)
└── examples/                # Example usage (if applicable)
```

## Naming conventions

| Item             | Convention        | Example                    |
|------------------|-------------------|----------------------------|
| Crates           | `snake_case`      | `my_crate`                 |
| Modules          | `snake_case`      | `my_module`                |
| Types / Traits   | `PascalCase`      | `MyStruct`, `MyTrait`      |
| Functions        | `snake_case`      | `do_something()`           |
| Constants        | `SCREAMING_SNAKE` | `MAX_RETRIES`              |
| Local variables  | `snake_case`      | `item_count`               |
| Type parameters  | Single uppercase  | `T`, `E`, `K`, `V`        |
| Lifetimes        | Short lowercase   | `'a`, `'de`                |

## Error handling

- Prefer `Result<T, E>` over `panic!()` for recoverable errors.
- Use `thiserror` for library error types, `anyhow` for application-level errors.
- Avoid `.unwrap()` and `.expect()` in production code. Propagate with `?`.
- Define custom error types when a module has more than one failure mode.
- When a fallible function consumes an argument, return it inside the error type so callers can recover it.

## Ownership and borrowing

- Prefer borrowing (`&T`, `&mut T`) over transferring ownership when the caller doesn't need to give up the value.
- Use `Clone` sparingly — never just to satisfy the borrow checker.
- Prefer `&str` over `String`, `&[T]` over `&Vec<T>`, and `&T` over `&Box<T>` in function parameters.
- Use `Cow<'_, str>` when a function may or may not need to allocate.
- Use `mem::take` or `mem::replace` to move values out of mutable references without cloning.

## Structs and enums

- Derive common traits where appropriate: `Debug`, `Clone`, `PartialEq`, `Eq`, `Hash`, `Default`.
- Use the builder pattern for structs with many optional fields.
- Prefer enums over boolean flags for state representation.
- Use `#[non_exhaustive]` on public structs and enums that may grow over time.
- Use the newtype pattern for type safety wrappers around primitives (zero-cost abstraction).
- Decompose large structs into smaller ones when the borrow checker prevents independent field access.

## Testing

- All implemented functionality must have corresponding tests.
- Tests must validate behavior, not just assert `true`.
- Use descriptive test names that explain what is being tested.
- Test both the happy path and error/edge cases.

```bash
cargo test                  # Run all tests
cargo test test_name        # Run a specific test
cargo test -- --nocapture   # Run tests with output
```

## Design principles

- **KISS**: Simplicity should be a key goal in design.
- **YAGNI**: Don't add functionality until it is necessary.
- **DRY**: Every piece of knowledge should have a single, authoritative representation.
- **SOLID**: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion.
- **Composition over inheritance**: Favour traits and composition over emulating inheritance.
- **Command-Query Separation**: Functions should either return data or produce side effects, not both.
- **Principle of Least Astonishment**: Components should behave the way most users expect.

## Performance

- Avoid unnecessary heap allocations. Prefer stack allocation and slices.
- Use `&[T]` instead of `&Vec<T>` and `&str` instead of `&String` in function signatures.
- Profile before optimizing. Use `cargo bench` and tools like `criterion`.
- Stay lazy with iterators. Avoid `.collect()`-ing unnecessarily.

## Dependencies

- Prefer well-maintained, widely-used crates. Pin versions in `Cargo.toml`.
- Minimize the dependency tree. Avoid adding crates for trivial functionality.
- Use `cargo audit` to check for known vulnerabilities.

## Documentation

- All public items (`pub`) must have doc comments.
- Include examples in doc comments for public functions.
- Use module-level documentation at the top of files.

## Anti-patterns

Common but counterproductive solutions. **Avoid them.**

### Clone to satisfy the borrow checker

Do not resolve borrow checker errors by cloning without understanding the consequences. Cloning creates independent copies — changes to one are not reflected in the other.

If the borrow checker complains, first understand the ownership issue. Use `mem::take`, restructure borrows, or redesign the data flow.

**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.

### `#![deny(warnings)]`

Do not use `#![deny(warnings)]` in crate roots. New compiler versions may introduce new warnings, breaking builds unexpectedly.

Instead, deny specific named lints explicitly, or use `RUSTFLAGS="-D warnings"` in CI. This preserves Rust's stability guarantees while still enforcing lint discipline.

### Deref polymorphism

Do 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.

It 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.

Instead, use composition with explicit delegation methods, or use traits for shared behaviour.

## Further reference

Before 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.
