--- name: rust-best-practices description: > Best-practices for Rust software. Load into a workstream whenever you need to read or write a Rust (.rs) file. --- # Best practices for programming Rust - Run `cargo check`, `cargo clippy`, and `cargo clippy` before comitting to git. - Don't ignore warnings from cargo. - Don't supress warnings using e.g. `#[allow(warning)]` - Never use `unsafe` without asking the user for explicit permission. When setting up a new rust project, enable these lints: ```toml [lints.clippy] allow_attributes = "warn" as_ptr_cast_mut = "warn" as_underscore = "warn" borrow_as_ptr = "warn" implicit_clone = "warn" undocumented_unsafe_blocks = "warn" unicode_not_nfc = "warn" unused_async = "deny" wildcard_dependencies = "deny" [lints.rust] absolute_paths_not_starting_with_crate = "deny" explicit_outlives_requirements = "warn" macro_use_extern_crate = "deny" missing_abi = "deny" non_ascii_idents = "forbid" rust_2018_idioms = { level = "deny", priority = -1 } single_use_lifetimes = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" ``` ## Newtypes and "Parse, Don't Validate" Use newtypes to make invalid states unrepresentable. Instead of passing raw strings and validating at every use site, parse once into a validated type. ### Pattern ```rust pub struct Email(String); // private field prevents construction without validation impl Email { pub fn parse(s: &str) -> Result { if !s.contains('@') { return Err(ParseError::InvalidEmail); } Ok(Self(s.to_owned())) } pub fn as_str(&self) -> &str { &self.0 } } ``` ### Guidelines - **Private fields**: Force construction through `parse()` only - **Parse at boundaries**: Validate at system edges (API handlers, CLI args, config loading) - **Use the type in signatures**: `fn create_user(email: Email)` not `fn create_user(email: &str)` - **No re-validation**: Once parsed, the type guarantees validity ### When to Apply - Email addresses, passwords, phone numbers - File paths, URLs, identifiers - Any domain value with invariants - Configuration values requiring validation ### Anti-pattern to Avoid ```rust // BAD: validation scattered, easy to forget fn handler(email: &str) { if !validate_email(email) { return Err(...); } create_user(email) // still just &str, could be invalid } ```