1---2name: rust-api-guidelines3description: Idiomatic Rust API design, naming, traits, error handling, and type safety. Use when writing or reviewing Rust code, designing library APIs, or implementing traits and error types.4---56# Rust API Guidelines78Apply these rules when writing or reviewing Rust code. See [REFERENCE.md](REFERENCE.md) for full details.910## Top LLM Violations1112| # | Guideline | Wrong | Right |13|---|-----------|-------|-------|14| 1 | C-COMMON-TRAITS | No derives on public types | `#[derive(Debug, Clone, PartialEq)]` minimum |15| 2 | C-QUESTION-MARK | `.unwrap()` in lib/example code | `?` with `Result` return types |16| 3 | C-GENERIC | `fn f(v: &Vec<T>)`, `fn f(s: &String)` | `fn f(v: &[T])`, `fn f(s: &str)` |17| 4 | C-GETTER | `fn get_name(&self) -> &str` | `fn name(&self) -> &str` |18| 5 | C-GOOD-ERR | `Result<T, ()>`, `Result<T, String>` | Domain error enum with `thiserror` |19| 6 | C-CONV | Wrong conversion prefix | See Conversion Naming below |20| 7 | C-DEREF | `Deref` on wrapper types | Only for smart pointers (`Box`, `Arc`) |21| 8 | C-STRUCT-BOUNDS | `struct Foo<T: Debug + Clone>` | `struct Foo<T>` — bounds on `impl` only |22| 9 | C-CALLER-CONTROL | Hidden `.clone()`, wrong ownership | Take owned when you need it, borrow when you don't |23| 10 | C-STRUCT-PRIVATE | All `pub` fields on library structs | Private fields + `new()` + getters |24| 11 | C-FAILURE | No error docs | `# Errors`, `# Panics`, `# Safety` sections |25| 12 | C-BUILDER | Struct with 5+ pub config fields | Builder: `FooBuilder::new().bar(1).build()` |26| 13 | C-NEWTYPE | `user_id: u64`, `timeout: u64` | `struct UserId(u64)`, `struct Timeout(Duration)` |27| 14 | C-CUSTOM-TYPE | `fn connect(use_tls: bool)` | `enum TlsMode { Enabled, Disabled }` |28| 15 | C-NEWTYPE-HIDE | `-> Enumerate<Skip<Map<...>>>` | `-> impl Iterator<Item = T>` or newtype |2930## Conversion Naming3132| Prefix | Cost | Ownership | Example |33|--------|------|-----------|---------|34| `as_` | Free | `&self -> &T` | `fn as_str(&self) -> &str` |35| `to_` | Expensive | `&self -> T` (allocates) | `fn to_string(&self) -> String` |36| `into_` | Variable | `self -> T` (consumes) | `fn into_inner(self) -> T` |37| `from_` | Variable | `T -> Self` (constructs) | `fn from_bytes(v: Vec<u8>) -> Self` |3839## Trait Checklist4041| Type category | Always derive | When applicable |42|---------------|---------------|-----------------|43| Public structs | `Debug`, `Clone` | `Default`, `PartialEq`, `Eq`, `Hash` |44| Error types | `Debug` + manual `Display` + `Error` | `Clone`, must be `Send + Sync + 'static` |45| ID / newtype | `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `Hash` | `Display`, `Ord` |46| Config / options | `Debug`, `Clone`, `Default` | `PartialEq` |47| API boundary types | `Debug`, `Clone`, `PartialEq`, `Serialize`, `Deserialize` | `JsonSchema` |4849## Generic Parameters5051| Instead of | Accept | Why |52|------------|--------|-----|53| `&Vec<T>` | `&[T]` | Works with arrays, slices, Vec |54| `&String` | `&str` | Works with literals, String, Cow |55| `&Box<T>` | `&T` | Box is transparent for borrows |56| `String` param | `impl Into<String>` | Accepts `&str` without caller `.to_string()` |57| `PathBuf` param | `impl AsRef<Path>` | Accepts `&str`, `&Path`, `PathBuf` |58| `Vec<T>` param | `impl IntoIterator<Item = T>` | Accepts arrays, slices, iterators |5960## Type Design6162- **Newtype IDs**: `#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] #[serde(transparent)] pub struct JobId(pub Uuid);`63- **Enum state machines**: States as enum variants with embedded data. No struct-with-booleans/optionals.64- **Validated newtypes**: Private fields + `fn new(...) -> Result<Self, Error>` + custom `Deserialize` that calls `new`.6566## Error Handling6768- Define domain error enums with `#[derive(Debug, thiserror::Error)]`, specific variants with context fields.69- With `#[source]`, don't embed `{err}` in `#[error("...")]` — the error chain formatter appends it. Embedding causes double-printing.7071## Functions7273- Target 10-30 lines per function. Decompose longer functions into well-named helpers.74- Value composition and testability: each function does one thing, returns a value, is independently testable.7576## Async / Tokio7778**Apply only to applications already using tokio. Libraries must not embed a runtime.**7980| Pattern | Rule |81|---------|------|82| Event loop | Single `select!` returning typed action enum, clean `loop { let action = select().await; apply(action).await; }` |83| Futurelock | Never hold locks across `select!` branches. Use channels + `spawn` instead. |84| Cancel-safety | `scopeguard::guard()` to restore invariants on drop. Defuse with `ScopeGuard::into_inner()` on success. |85| Channels | Bounded `mpsc` (backpressure), `oneshot` (request-reply), `watch` (broadcast/cancel). Avoid unbounded mpsc. |86| Lock scope | Clone data out of mutex before `.await`. Never hold `MutexGuard` across await points. |8788## Clippy8990- Never add `#[allow(clippy::...)]` to suppress a Clippy warning. Fix the underlying issue instead.9192## Testing9394- Write tests before implementation. Use `#[cfg(test)] mod tests` in the same file.95- Wrap external dependencies behind traits for testability. **Ask the developer** about the scope: IO boundaries only, all external deps, or full ports-and-adapters.9697## Full Reference9899- [Rust API Guidelines](https://rust-lang.github.io/api-guidelines) | [Checklist](https://rust-lang.github.io/api-guidelines/checklist.html)100- [REFERENCE.md](REFERENCE.md) — all 54 guidelines + pattern details