AGENTS.md for coding agents
This codebase follows the Google Rust Style Guide. Formatting and dozens of rules are enforced by rustfmt, clippy and cargo-deny. You do not need to memorize those; the tools will tell you. This file covers the rules no tool can check. Follow them when writing or reviewing Rust here.
Before you finish
Section titled “Before you finish”Run these and fix everything they report:
cargo +nightly fmt # nightly applies the guide's unstable rustfmt optionscargo clippy --workspace --all-targets -- -D warningscargo test --workspacecargo deny check bans # if cargo-deny is installedWhen a lint fires
Section titled “When a lint fires”-
Fix the code. This is the answer almost every time.
-
If the guide itself grants an exception (listed under each rule below and in the guide), suppress the lint at the narrowest scope with a reason the reviewer can check:
#[expect(clippy::exhaustive_structs, reason = "options struct; callers rely on struct update syntax")] -
Never use
#[allow(...)]without areason, never use#[rustfmt::skip]for personal taste, and never edit the lint configuration (Cargo.toml[lints],clippy.toml,rustfmt.toml,deny.toml) to make a warning go away.
Prefer #[expect] over #[allow]: it fails once the suppression is no longer needed.
Library code or application code?
Section titled “Library code or application code?”Decide first. For library code (used by other projects, solving a general problem) the API rules below are binding. For application code (one program, a specific problem) they are strong recommendations, and simplicity wins: do not generalize prematurely.
Rules tools cannot check
Section titled “Rules tools cannot check”Code organization
Section titled “Code organization”-
Non-leaf modules only declare and re-export.
lib.rs,mod.rsand other modules with children holdmoddeclarations andpub usere-exports, plus at most a little glue. Put implementations in leaf modules. -
Name a leaf module after its primary item. A module whose main item is
RetryPolicyisretry_policy.rs. -
No glob imports and no preludes. Clippy catches most globs. The guide allows
use super::*in inline modules,use Enum::*inside functions that mostly handle that enum, andpub use submodule::*in non-leaf modules. Do not define apreludemodule. -
Import traits locally inside the function that needs them, unless the file uses the trait throughout.
fn parse_port(text: &str) -> Result<u16, ParseIntError> {use std::str::FromStr;u16::from_str(text)} -
Call free functions through their module; use types unqualified.
// Baduse std::mem::swap;let pattern = regex::Regex::new(r"\d+")?;swap(&mut left, &mut right);// Gooduse std::mem;use regex::Regex;let pattern = Regex::new(r"\d+")?;mem::swap(&mut left, &mut right);Exceptions: ambiguous names may stay qualified (
io::Error) or be renamed (use std::io::Error as IoError), as may large families of types, macros and generated code.
Formatting
Section titled “Formatting”- Use
#[rustfmt::skip]only for deliberate manual alignment (for example a matrix literal) or generated code. - Write invisible characters as escapes (
"\u{200D}"), never literally.
Ownership and borrowing
Section titled “Ownership and borrowing”- Avoid
RcandArc. Model ownership with unique owners and borrows first. Shared ownership is allowed only when the alternative is unreasonably complex, and the#[expect(clippy::disallowed_types, reason = "...")]must say why. - Clone only when you need a copy. Do not use
.clone()to get past the borrow checker; restructure first. Cloning anRc/Archandle is fine.
Function inputs and outputs
Section titled “Function inputs and outputs”-
Return iterators from library functions (
impl Iterator<Item = T>) instead ofVec<T>, unless callers almost always need aVec, the function naturally builds one, results need random access, or the function’s purpose is to build a collection. Application code may returnVec<T>when that is simpler. -
Accept
impl IntoIteratorin library functions that only iterate their input. Application code may take&[T]. -
impl Into<T>parameters only in library APIs where call-site ergonomics matter most. Application code should almost never use them. -
Enum, not
bool, when the meaning is not obvious at the call site. Clippy only flags two or more bools; a single unclear bool is your call.// Badfile.open(path, true);// Goodfile.open(path, OpenMode::CreateIfMissing);If a
boolstays, label it at the call site:connect(addr, /* retry= */ false). -
Pick the right error channel.
Optionfor an obvious, expected absence.Resultfor recoverable failures where the caller needs to know why (I/O, network, parsing with detail).panic!only for bugs and broken invariants, and document it under# Panics.
- Newtype instead of type alias when the type has different meaning, needs its own API, may change representation, or could be confused with another value of the same underlying type (
struct UserId(u64), nottype UserId = u64). - Named fields instead of tuples. No
(String, u32, bool)return values or multi-field tuple structs. One-field newtypes and very local tuples are fine.
Traits
Section titled “Traits”- Library code anticipates trait impls; application code adds them when needed.
- Implement the standard trait (
FromStr,Display,From,Iterator,Default) instead of an inherent method with the same purpose. - Prefer
#[derive]over hand-written impls. - Generics over
dyn Traitin library parameters (T: Traitorimpl Trait), unless code size matters. - Define a trait only for generic bounds, for
impl Trait/dyn Trait, or as an extension trait. Never just to group methods; use an inherentimpl. - Extension traits end in
Ext(PathBufExt) and belong to the team that owns the extended type. - Trait checklist for new types:
- Error types:
Copywhen no field prevents it, and#[must_use]. Copywhen it will stayCopy;Cloneunless the type owns an external resource.Defaultwhen there isnew()or an obvious default state.- Prefer
PartialEqoverEq, andPartialOrdoverOrd, unless you need the stronger guarantee. Hash+Eqfor likely map keys.
- Error types:
- Pair a primary
Fromimpl with a named constructor (new,new_from_xorfrom_x). - Options structs implement
Defaultso callers can use..Default::default().
Concurrency
Section titled “Concurrency”- Never read through a poisoned lock. Propagate or panic; do not call
into_inner/get_ref/get_muton aPoisonError(clippy flags those calls).
Macros
Section titled “Macros”Prefer, in order: plain functions, types and traits; macro_rules!; other code generation; procedural macros. Justify every macro.
Naming
Section titled “Naming”- Conversions:
as_(cheap borrow),to_(expensive or owned),into_(consumesself). - Constructors:
new(),new_with_x()for alternatives,try_new()when it returnsResult. Value conversions:from_x()alongside aFromimpl. - Accessors:
foo()getter (noget_prefix),foo_mut(),set_foo(value),take_foo()(clears and returns),replace_foo(value)(returns the old value),with_foo(|foo| ..)/with_mut_foo(|foo| ..)for scoped access through a lock orRefCell. - Lifetimes: descriptive names on types (
'source, not'a). Use'_where it works; one or two single-letter lifetimes are fine for a single method’s input-to-output relationship or a localfor<'a>bound. - Generic parameters: short and tied to the main bound (
R: Read,K,V). UseT/Uonly for unconstrained generics. - No abbreviations an outsider would not know, and no dropped letters (
message, notmsg;count, notcnt). Widely known terms (Ip,Http,Utf8) are fine.
Documentation
Section titled “Documentation”- Doc comments (
///) on every public item, written in full sentences with Markdown;//!at the top of a file for the module. - Say what the code does not: skip
/// Removes an element.onremove_element. - Document
# Panics,# Errorsand# Safetysections. Include examples where they help readers see how the crate fits together. //for implementation notes and TODOs. Mixing///and//on the same item is fine.
Other features
Section titled “Other features”- Unsafe: only when there is no safe way. Wrap it in a small safe abstraction away from business logic, add a
// SAFETY:comment explaining why it is sound, and record why safe code was not enough (with benchmarks if the reason is performance). - No unstable features.
- Never use
#[doc(hidden)]items from code you do not own. const fnin public APIs only if you will keep itconst; removingconstis a breaking change.- Operator overloading only for mathematical or logical meaning, never as shorthand.
#[non_exhaustive]on public enums and on public structs with all-public fields. Exceptions: fixed ontologies (Color { Red, Green, Blue }), two-state enums that could be a bool, options structs that use struct update syntax, or types whose owners will fix every caller themselves.#[must_use]on functions whose result must be used (pure functions, critical status), and on types that are errors or lazy computations.- No code before
main()(ctor,inventory,static_init).
Implementation
Section titled “Implementation”-
Annotate types where inference hides them, especially after
.into(),.parse(),.collect()and long iterator chains. Put the type on thelet, not in a turbofish.let port: u16 = text.parse()?; // not text.parse::<u16>()? -
Prove instead of unwrapping:
if let/let ... elseinstead ofis_some()thenunwrap(). When a panic is unavoidable, useexpect("why this cannot fail"). -
When matching a
#[non_exhaustive]enum from another crate, list every known variant and put the ones you ignore in the wildcard arm (Shape::Triangle | _ => {}). Useif letwhen you care about one variant. -
Give
assert!a message that explains why the condition holds or what the caller did wrong, never one that restates the condition. -
Iterator chains stay short. Use a
forloop when a chain mutates outside state or branches. Never build a chain in aforheader; bind it to a named variable first. Avoidfor_each.let urgent_bugs = bugs.iter().filter(|bug| bug.priority <= Priority::P1);for bug in urgent_bugs {assign(bug);} -
No
panic::catch_unwind, except as defense in depth at an entry point in a codebase that already works to avoid panics. -
format!/write!to join three or more strings and to fill templates, not+chains orstr::replaceon placeholder text.
Unofficial community project, not affiliated with or endorsed by Google. TheGoogle Rust Style Guide is published by Google under CC-BY 3.0. This kit is Apache-2.0.