Rust Conventions
Apply these rules to all Rust code written in this workspace. Prioritise readability, safety,
and maintainability in that order.
Naming (RFC 430)
Follow RFC 430 naming conventions throughout:
| Item |
Convention |
Example |
| Types, traits, enums |
UpperCamelCase |
TaskGraph, PolicyAction |
| Functions, methods, variables |
snake_case |
check_tool, max_calls |
| Constants, statics |
SCREAMING_SNAKE_CASE |
MAX_NODES, DEFAULT_TIMEOUT |
| Modules |
snake_case |
mod graph_store |
| Lifetimes |
short lowercase |
'a, 'src |
| Type parameters |
short UpperCamelCase |
T, E, Fut |
Ownership and Borrowing
- Prefer
&T over cloning unless ownership transfer is required.
- Use
&mut T when you need to mutate borrowed data.
- Annotate lifetimes explicitly when the compiler cannot infer them.
Rc<T> for single-threaded reference counting; Arc<T> for multi-threaded.
RefCell<T> for interior mutability in single-threaded contexts;
Mutex<T> or RwLock<T> for multi-threaded.
- Prefer borrowing and zero-copy operations to avoid unnecessary allocations.
- Use
&str instead of String for function parameters when ownership is not required.
Error Handling
- Use
Result<T, E> for all recoverable errors. Never unwrap() or expect() in library
code — return Result instead.
- Use the
? operator for error propagation. Avoid unwrap() unless in a test or a context
where panic is explicitly acceptable.
- Create custom error types with
thiserror; use anyhow for application-level error
aggregation.
- Use
Option<T> for values that may legitimately be absent.
- Provide meaningful error messages with context. Error types must implement
Debug and
Display at minimum.
- Validate function arguments and return appropriate errors for invalid input.
panic! is only for unrecoverable programmer errors (broken invariants). Never panic in
response to external input.
// Prefer
fn load(path: &str) -> anyhow::Result<Config> {
let text = std::fs::read_to_string(path)?;
Ok(serde_json::from_str(&text)?)
}
// Avoid
fn load(path: &str) -> Config {
let text = std::fs::read_to_string(path).unwrap(); // panics on missing file
serde_json::from_str(&text).unwrap()
}
Iterators and Collections
- Use iterators instead of index-based loops — they are often faster, safer, and more
expressive.
- Avoid premature
.collect() — keep iterator chains lazy until a collection is actually
needed.
- Prefer
.filter_map() over .filter().map() when the two operations can be combined.
- Use
.fold() for accumulation rather than a mutable variable outside the loop.
Patterns to Follow
- Modules and visibility: Use
mod + pub to encapsulate logic. Keep internal types
private; expose only what callers need.
- Enums over flags: Prefer enum variants over
bool parameters or integer flags. Type
safety catches misuse at compile time.
- Builders for complex construction: Use the builder pattern when a struct has more than
3–4 optional fields.
- Trait abstraction: Implement traits to abstract services and external dependencies,
making them replaceable in tests.
- Async: Structure async code with
async/await and tokio. Avoid blocking in async
contexts.
- Data parallelism: Use
rayon for CPU-bound parallel iteration.
- Binary/library split: Keep
main.rs thin — move all logic into lib.rs and its
modules. This enables integration tests and downstream crate reuse.
Patterns to Avoid
unwrap() / expect() in non-test, non-prototype code.
- Panics in library code.
- Global mutable state — use dependency injection or thread-safe containers.
- Deeply nested
if/match blocks — extract functions or use combinators.
- Ignoring compiler warnings — treat
-D warnings as the standard in CI.
unsafe without necessity and thorough documentation.
- Overusing
.clone() where a borrow would suffice.
- Unnecessary heap allocations in hot paths.
Common Traits
Eagerly derive or implement these where appropriate:
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MyType { ... }
| Trait |
When |
Debug |
Every public type — required |
Clone |
When callers need owned copies |
PartialEq, Eq |
For value comparison and use in collections |
Hash |
When used as a HashMap/HashSet key |
Default |
When a zero-value construction is meaningful |
Display |
For user-facing output |
From / Into |
For idiomatic type conversions |
AsRef, AsMut |
For generic borrowing interfaces |
FromIterator, Extend |
For collection types |
Send and Sync are auto-implemented by the compiler. Avoid manual unsafe impl Send/Sync
unless you fully understand the invariants.
API Design
- Newtypes: Use newtypes to distinguish values that share the same primitive type
(
struct UserId(u64) vs struct OrderId(u64)).
- Private fields: Structs should have private fields by default. Expose state through
methods to preserve invariants.
- Sealed traits: Use the sealed trait pattern to prevent downstream implementations of
traits not intended for extension.
- Method receivers: Functions with a clear receiver should be methods, not free functions.
Deref/DerefMut: Only smart pointers should implement these. Do not implement Deref
to gain method inheritance.
- Argument types: Prefer specific types over generic
bool parameters. A bool argument
at a call site conveys no intent (create(true) vs create(CreateMode::Overwrite)).
Async
- Use
tokio as the async runtime. Do not mix runtimes.
- Never call
.block_on() inside an async context.
- Use
tokio::spawn for background tasks; propagate JoinHandle errors.
- Prefer
tokio::fs over std::fs in async code.
- Keep
async boundaries at the edges — pure computation should be synchronous.
Code Style
- Run
rustfmt (cargo fmt --all) before every commit.
- Run
cargo clippy -- -D warnings and fix all warnings.
- Keep lines at or under 100 characters.
- Place doc comments (
///) immediately above the item they document.
- Use
//! for module-level documentation.
- Document error conditions, panic scenarios, and safety considerations.
Testing
- Write unit tests in
#[cfg(test)] modules in the same file as the code under test.
- Write integration tests in
tests/ with descriptive file names.
- Use
cargo nextest run (preferred over cargo test) for test filtering and parallelism.
- Test edge cases explicitly — not just the happy path.
- Examples in doc comments must compile and use
?, not unwrap().
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_tool_denies_blocked() {
let policy = GovernancePolicy {
blocked_tools: vec!["shell_exec".into()],
..Default::default()
};
assert_eq!(policy.check_tool("shell_exec"), PolicyAction::Deny);
}
}
Project Organisation
- Use semantic versioning in
Cargo.toml.
- Include
description, license, repository, keywords, categories in every crate.
- Use feature flags for optional functionality.
- Keep
main.rs and lib.rs minimal — move logic to named modules.
- Organise modules as named files (
context.rs) rather than mod.rs directories where
possible (cleaner in editor file pickers).
Quality Checklist
Before submitting any Rust code:
Related
skills/agent-governance/SKILL.md — Rust implementations of governance patterns
skills/systematic-debugging/SKILL.md — debugging approach for Rust-specific issues
skills/verification-before-completion/SKILL.md — CI gate checklist
Source: 89jobrien/godmode — distributed by TomeVault.
1---2name: 89jobrien-godmode-godmode-rust-conventions3description: Rust Conventions4---56# Rust Conventions78Apply these rules to all Rust code written in this workspace. Prioritise readability, safety,9and maintainability in that order.1011---1213## Naming (RFC 430)1415Follow RFC 430 naming conventions throughout:1617| Item | Convention | Example |18| ----------------------------- | ---------------------- | ------------------------------ |19| Types, traits, enums | `UpperCamelCase` | `TaskGraph`, `PolicyAction` |20| Functions, methods, variables | `snake_case` | `check_tool`, `max_calls` |21| Constants, statics | `SCREAMING_SNAKE_CASE` | `MAX_NODES`, `DEFAULT_TIMEOUT` |22| Modules | `snake_case` | `mod graph_store` |23| Lifetimes | short lowercase | `'a`, `'src` |24| Type parameters | short `UpperCamelCase` | `T`, `E`, `Fut` |2526---2728## Ownership and Borrowing2930- Prefer `&T` over cloning unless ownership transfer is required.31- Use `&mut T` when you need to mutate borrowed data.32- Annotate lifetimes explicitly when the compiler cannot infer them.33- `Rc<T>` for single-threaded reference counting; `Arc<T>` for multi-threaded.34- `RefCell<T>` for interior mutability in single-threaded contexts;35 `Mutex<T>` or `RwLock<T>` for multi-threaded.36- Prefer borrowing and zero-copy operations to avoid unnecessary allocations.37- Use `&str` instead of `String` for function parameters when ownership is not required.3839---4041## Error Handling4243- Use `Result<T, E>` for all recoverable errors. Never `unwrap()` or `expect()` in library44 code — return `Result` instead.45- Use the `?` operator for error propagation. Avoid `unwrap()` unless in a test or a context46 where panic is explicitly acceptable.47- Create custom error types with `thiserror`; use `anyhow` for application-level error48 aggregation.49- Use `Option<T>` for values that may legitimately be absent.50- Provide meaningful error messages with context. Error types must implement `Debug` and51 `Display` at minimum.52- Validate function arguments and return appropriate errors for invalid input.53- `panic!` is only for unrecoverable programmer errors (broken invariants). Never panic in54 response to external input.5556```rust57// Prefer58fn load(path: &str) -> anyhow::Result<Config> {59 let text = std::fs::read_to_string(path)?;60 Ok(serde_json::from_str(&text)?)61}6263// Avoid64fn load(path: &str) -> Config {65 let text = std::fs::read_to_string(path).unwrap(); // panics on missing file66 serde_json::from_str(&text).unwrap()67}68```6970---7172## Iterators and Collections7374- Use iterators instead of index-based loops — they are often faster, safer, and more75 expressive.76- Avoid premature `.collect()` — keep iterator chains lazy until a collection is actually77 needed.78- Prefer `.filter_map()` over `.filter().map()` when the two operations can be combined.79- Use `.fold()` for accumulation rather than a mutable variable outside the loop.8081---8283## Patterns to Follow8485- **Modules and visibility**: Use `mod` + `pub` to encapsulate logic. Keep internal types86 private; expose only what callers need.87- **Enums over flags**: Prefer enum variants over `bool` parameters or integer flags. Type88 safety catches misuse at compile time.89- **Builders for complex construction**: Use the builder pattern when a struct has more than90 3–4 optional fields.91- **Trait abstraction**: Implement traits to abstract services and external dependencies,92 making them replaceable in tests.93- **Async**: Structure async code with `async/await` and `tokio`. Avoid blocking in async94 contexts.95- **Data parallelism**: Use `rayon` for CPU-bound parallel iteration.96- **Binary/library split**: Keep `main.rs` thin — move all logic into `lib.rs` and its97 modules. This enables integration tests and downstream crate reuse.9899---100101## Patterns to Avoid102103- `unwrap()` / `expect()` in non-test, non-prototype code.104- Panics in library code.105- Global mutable state — use dependency injection or thread-safe containers.106- Deeply nested `if`/`match` blocks — extract functions or use combinators.107- Ignoring compiler warnings — treat `-D warnings` as the standard in CI.108- `unsafe` without necessity and thorough documentation.109- Overusing `.clone()` where a borrow would suffice.110- Unnecessary heap allocations in hot paths.111112---113114## Common Traits115116Eagerly derive or implement these where appropriate:117118```rust119#[derive(Debug, Clone, PartialEq, Eq, Hash)]120pub struct MyType { ... }121```122123| Trait | When |124| ------------------------ | -------------------------------------------- |125| `Debug` | Every public type — required |126| `Clone` | When callers need owned copies |127| `PartialEq`, `Eq` | For value comparison and use in collections |128| `Hash` | When used as a `HashMap`/`HashSet` key |129| `Default` | When a zero-value construction is meaningful |130| `Display` | For user-facing output |131| `From` / `Into` | For idiomatic type conversions |132| `AsRef`, `AsMut` | For generic borrowing interfaces |133| `FromIterator`, `Extend` | For collection types |134135`Send` and `Sync` are auto-implemented by the compiler. Avoid manual `unsafe impl Send/Sync`136unless you fully understand the invariants.137138---139140## API Design141142- **Newtypes**: Use newtypes to distinguish values that share the same primitive type143 (`struct UserId(u64)` vs `struct OrderId(u64)`).144- **Private fields**: Structs should have private fields by default. Expose state through145 methods to preserve invariants.146- **Sealed traits**: Use the sealed trait pattern to prevent downstream implementations of147 traits not intended for extension.148- **Method receivers**: Functions with a clear receiver should be methods, not free functions.149- **`Deref`/`DerefMut`**: Only smart pointers should implement these. Do not implement `Deref`150 to gain method inheritance.151- **Argument types**: Prefer specific types over generic `bool` parameters. A `bool` argument152 at a call site conveys no intent (`create(true)` vs `create(CreateMode::Overwrite)`).153154---155156## Async157158- Use `tokio` as the async runtime. Do not mix runtimes.159- Never call `.block_on()` inside an async context.160- Use `tokio::spawn` for background tasks; propagate `JoinHandle` errors.161- Prefer `tokio::fs` over `std::fs` in async code.162- Keep `async` boundaries at the edges — pure computation should be synchronous.163164---165166## Code Style167168- Run `rustfmt` (`cargo fmt --all`) before every commit.169- Run `cargo clippy -- -D warnings` and fix all warnings.170- Keep lines at or under 100 characters.171- Place doc comments (`///`) immediately above the item they document.172- Use `//!` for module-level documentation.173- Document error conditions, panic scenarios, and safety considerations.174175---176177## Testing178179- Write unit tests in `#[cfg(test)]` modules in the same file as the code under test.180- Write integration tests in `tests/` with descriptive file names.181- Use `cargo nextest run` (preferred over `cargo test`) for test filtering and parallelism.182- Test edge cases explicitly — not just the happy path.183- Examples in doc comments must compile and use `?`, not `unwrap()`.184185```rust186#[cfg(test)]187mod tests {188 use super::*;189190 #[test]191 fn check_tool_denies_blocked() {192 let policy = GovernancePolicy {193 blocked_tools: vec!["shell_exec".into()],194 ..Default::default()195 };196 assert_eq!(policy.check_tool("shell_exec"), PolicyAction::Deny);197 }198}199```200201---202203## Project Organisation204205- Use semantic versioning in `Cargo.toml`.206- Include `description`, `license`, `repository`, `keywords`, `categories` in every crate.207- Use feature flags for optional functionality.208- Keep `main.rs` and `lib.rs` minimal — move logic to named modules.209- Organise modules as named files (`context.rs`) rather than `mod.rs` directories where210 possible (cleaner in editor file pickers).211212---213214## Quality Checklist215216Before submitting any Rust code:217218- [ ] Naming follows RFC 430 throughout219- [ ] All public types derive or implement `Debug`220- [ ] Error handling uses `Result<T, E>` — no bare `unwrap()` in non-test code221- [ ] All public items have `///` rustdoc with at least one sentence222- [ ] Tests cover the happy path and at least one failure/edge case223- [ ] No `unsafe` without a `// SAFETY:` comment explaining the invariant224- [ ] `cargo fmt --all` — no format diff225- [ ] `cargo clippy -- -D warnings` — zero warnings226- [ ] `cargo nextest run` — all green227228---229230## Related231232- `skills/agent-governance/SKILL.md` — Rust implementations of governance patterns233- `skills/systematic-debugging/SKILL.md` — debugging approach for Rust-specific issues234- `skills/verification-before-completion/SKILL.md` — CI gate checklist235236---237> Source: [89jobrien/godmode](https://github.com/89jobrien/godmode) — distributed by [TomeVault](https://tomevault.io).238<!-- tomevault:4.0:skill_md:2026-06-15 -->