83 lines | 4.1 KB

Rust API Guidelines — Claude Code rules

You are designing a public Rust crate / library API. Follow the official Rust API Guidelines. The checklist codes (C-CASE, C-COMMON-TRAITS, …) are the source of truth; the rules below distill them.

Naming (RFC 430)

  • UpperCamelCase for types, traits, enum variants, and type parameters. Treat acronyms as one word: Uuid, not UUID.
  • snake_case for modules, functions, methods, locals, and macros (!).
  • SCREAMING_SNAKE_CASE for constants and statics.
  • Lifetimes are short and lowercase: 'a, 'de, 'src.
  • Getters drop the get_ prefix: fn first(&self), not get_first. Reserve get for the single obvious accessor (e.g. Cell::get). (C-GETTER)
  • Conversion methods follow cost + ownership: as_* (free, borrow→borrow), to_* (expensive, borrow→owned), into_* (consuming, owned→owned). (C-CONV)
  • Iterator-producing methods are iter / iter_mut / into_iter, returning types named Iter / IterMut / IntoIter. (C-ITER, C-ITER-TY)
  • Keep a consistent word order matching std (e.g. ParseIntError). (C-WORD-ORDER)
  • Cargo feature names have no placeholder words: use std, not use-std; crate names avoid -rs / -rust suffixes. (C-FEATURE)

Common traits & interop

  • Eagerly derive the standard traits when valid: Debug, Clone, Copy, PartialEq/Eq, PartialOrd/Ord, Hash, Default; implement Display where a human-facing form exists. Orphan rules mean downstream crates cannot add these later, so do it here. (C-COMMON-TRAITS)
  • Every public type implements Debug, and its output is non-empty. (C-DEBUG)
  • Conversions go through From, TryFrom, AsRef, AsMut. Never hand-write Into / TryInto — they come free via blanket impls. (C-CONV-TRAITS)
  • Collections implement FromIterator and Extend. (C-COLLECT)
  • Gate serde::Serialize / Deserialize behind a serde feature. (C-SERDE)
  • Keep types Send + Sync wherever the compiler allows. (C-SEND-SYNC)
  • Generic I/O takes R: Read / W: Write by value (callers pass &mut). (C-RW-VALUE)

Error types

  • Public error types implement std::error::Error and are Send + Sync + 'static with useful Debug and Display. (C-GOOD-ERR)
  • Document failure modes: # Errors for Result-returning fns, # Panics for panics, # Safety for unsafe fns. (C-FAILURE)
  • Destructors never fail or block: Drop is infallible (a panic in Drop during unwinding aborts). Expose a separate close() / flush() returning Result for fallible teardown. (C-DTOR-FAIL, C-DTOR-BLOCK)

Type safety

  • Use newtypes to give distinct static meaning to like-shaped values (Miles(f64) vs Kilometers(f64)). (C-NEWTYPE)
  • Convey intent through dedicated types/enums, not bare bool or Option arguments. (C-CUSTOM-TYPE)
  • Represent flag sets with the bitflags crate, not enums. (C-BITFLAG)
  • Offer a builder for types with many or optional inputs. (C-BUILDER)
  • Validate arguments — prefer types that make invalid states unrepresentable; otherwise check and return Result. (C-VALIDATE)

Future-proofing

  • Keep struct fields private; expose accessors/setters so the representation can evolve. (C-STRUCT-PRIVATE, C-NEWTYPE-HIDE)
  • Seal traits not meant for downstream impl via a private supertrait, so you can add methods without breaking. (C-SEALED)
  • Mark enums (and structs) you may extend with #[non_exhaustive] so adding variants/fields is non-breaking.
  • Do not duplicate derived trait bounds on the struct itself — adding a bound is a breaking change; deriving more traits is not. (C-STRUCT-BOUNDS)

Docs

  • Crate-level docs are thorough with examples; every public item has a rustdoc example. (C-CRATE-DOC, C-EXAMPLE)
  • Examples use ?, never unwrap() or try!. (C-QUESTION-MARK)
  • Hide internals with #[doc(hidden)] / pub(crate). (C-HIDDEN)

Don't

  • Don't hand-implement Into/TryInto, or add get_ to getters.
  • Don't expose public fields just to skip writing accessors.
  • Don't panic in Drop, and don't take out-parameters — return values instead.
  • Don't add a generic trait bound to a struct that you only need on its impls.