|
| 1 | +--- |
| 2 | +name: rust-best-practices |
| 3 | +description: > |
| 4 | + Guide for writing idiomatic Rust code based on Apollo GraphQL's best practices handbook. Use this skill when: |
| 5 | + (1) writing new Rust code or functions, |
| 6 | + (2) reviewing or refactoring existing Rust code, |
| 7 | + (3) deciding between borrowing vs cloning or ownership patterns, |
| 8 | + (4) implementing error handling with Result types, |
| 9 | + (5) optimizing Rust code for performance, |
| 10 | + (6) writing tests or documentation for Rust projects. |
| 11 | +license: MIT |
| 12 | +compatibility: Rust 1.70+, Cargo |
| 13 | +metadata: |
| 14 | + author: apollographql |
| 15 | + version: "1.1.0" |
| 16 | +allowed-tools: Bash(cargo:*) Bash(rustc:*) Bash(rustfmt:*) Bash(clippy:*) Read Write Edit Glob Grep |
| 17 | +--- |
| 18 | + |
| 19 | +# Rust Best Practices |
| 20 | + |
| 21 | +Apply these guidelines when writing or reviewing Rust code. Based on Apollo GraphQL's [Rust Best Practices Handbook](https://github.com/apollographql/rust-best-practices). |
| 22 | + |
| 23 | +## Best Practices Reference |
| 24 | + |
| 25 | +Before reviewing, familiarize yourself with Apollo's Rust best practices. Read ALL relevant chapters in the same turn in parallel. Reference these files when providing feedback: |
| 26 | + |
| 27 | +- [Chapter 1 - Coding Styles and Idioms](references/chapter_01.md): Borrowing vs cloning, Copy trait, Option/Result handling, iterators, comments |
| 28 | +- [Chapter 2 - Clippy and Linting](references/chapter_02.md): Clippy configuration, important lints, workspace lint setup |
| 29 | +- [Chapter 3 - Performance Mindset](references/chapter_03.md): Profiling, avoiding redundant clones, stack vs heap, zero-cost abstractions |
| 30 | +- [Chapter 4 - Error Handling](references/chapter_04.md): Result vs panic, thiserror vs anyhow, error hierarchies |
| 31 | +- [Chapter 5 - Automated Testing](references/chapter_05.md): Test naming, one assertion per test, snapshot testing |
| 32 | +- [Chapter 6 - Generics and Dispatch](references/chapter_06.md): Static vs dynamic dispatch, trait objects |
| 33 | +- [Chapter 7 - Type State Pattern](references/chapter_07.md): Compile-time state safety, when to use it |
| 34 | +- [Chapter 8 - Comments vs Documentation](references/chapter_08.md): When to comment, doc comments, rustdoc |
| 35 | +- [Chapter 9 - Understanding Pointers](references/chapter_09.md): Thread safety, Send/Sync, pointer types |
| 36 | + |
| 37 | +## Quick Reference |
| 38 | + |
| 39 | +### Borrowing & Ownership |
| 40 | +- Prefer `&T` over `.clone()` unless ownership transfer is required |
| 41 | +- Use `&str` over `String`, `&[T]` over `Vec<T>` in function parameters |
| 42 | +- Small `Copy` types (≤24 bytes) can be passed by value |
| 43 | +- Use `Cow<'_, T>` when ownership is ambiguous |
| 44 | + |
| 45 | +### Error Handling |
| 46 | +- Return `Result<T, E>` for fallible operations; avoid `panic!` in production |
| 47 | +- Never use `unwrap()`/`expect()` outside tests |
| 48 | +- Use `thiserror` for library errors, `anyhow` for binaries only |
| 49 | +- Prefer `?` operator over match chains for error propagation |
| 50 | + |
| 51 | +### Performance |
| 52 | +- Always benchmark with `--release` flag |
| 53 | +- Run `cargo clippy -- -D clippy::perf` for performance hints |
| 54 | +- Avoid cloning in loops; use `.iter()` instead of `.into_iter()` for Copy types |
| 55 | +- Prefer iterators over manual loops; avoid intermediate `.collect()` calls |
| 56 | + |
| 57 | +### Linting |
| 58 | +Run regularly: `cargo clippy --all-targets --all-features --locked -- -D warnings` |
| 59 | + |
| 60 | +Key lints to watch: |
| 61 | +- `redundant_clone` - unnecessary cloning |
| 62 | +- `large_enum_variant` - oversized variants (consider boxing) |
| 63 | +- `needless_collect` - premature collection |
| 64 | + |
| 65 | +Use `#[expect(clippy::lint)]` over `#[allow(...)]` with justification comment. |
| 66 | + |
| 67 | +### Testing |
| 68 | +- Name tests descriptively: `process_should_return_error_when_input_empty()` |
| 69 | +- One assertion per test when possible |
| 70 | +- Use doc tests (`///`) for public API examples |
| 71 | +- Consider `cargo insta` for snapshot testing generated output |
| 72 | + |
| 73 | +### Generics & Dispatch |
| 74 | +- Prefer generics (static dispatch) for performance-critical code |
| 75 | +- Use `dyn Trait` only when heterogeneous collections are needed |
| 76 | +- Box at API boundaries, not internally |
| 77 | + |
| 78 | +### Type State Pattern |
| 79 | +Encode valid states in the type system to catch invalid operations at compile time: |
| 80 | +```rust |
| 81 | +struct Connection<State> { /* ... */ _state: PhantomData<State> } |
| 82 | +struct Disconnected; |
| 83 | +struct Connected; |
| 84 | + |
| 85 | +impl Connection<Connected> { |
| 86 | + fn send(&self, data: &[u8]) { /* only connected can send */ } |
| 87 | +} |
| 88 | +``` |
| 89 | + |
| 90 | +### Documentation |
| 91 | +- `//` comments explain *why* (safety, workarounds, design rationale) |
| 92 | +- `///` doc comments explain *what* and *how* for public APIs |
| 93 | +- Every `TODO` needs a linked issue: `// TODO(#42): ...` |
| 94 | +- Enable `#![deny(missing_docs)]` for libraries |
0 commit comments