Patterns#

Rust’s type system encourages a handful of patterns that turn up in nearly every codebase.

Result + ?#

Propagate errors with the question-mark operator.

fn read_config(path: &Path) -> anyhow::Result<Config> {
    let text = std::fs::read_to_string(path)?;
    let cfg: Config = toml::from_str(&text)?;
    Ok(cfg)
}

Newtype Wrappers#

A tuple struct around a primitive gives you a distinct type for free.

struct UserId(u64);
struct Email(String);

This prevents mixing up arguments of compatible primitive types.

Builder#

Construct configurable values step by step.

let req = Request::builder()
    .method("GET")
    .header("Accept", "application/json")
    .uri("/v1/users")
    .build()?;

RAII / Drop#

Resources are released in Drop. There is no manual free or close in idiomatic Rust; when a value goes out of scope, its destructor runs.

{
    let _file = File::open("data")?;     // opened
}   // closed here

Iterators#

Prefer iterator chains over hand-written loops; they’re typically as fast and clearer to read.

From / Into / TryFrom#

Implement these traits to make conversions ergonomic.

impl From<&str> for UserId {
    fn from(s: &str) -> Self { UserId(s.parse().unwrap()) }
}

Visibility#

Items are private by default; mark them pub to expose them. Prefer narrow visibility (pub(crate), pub(super)) where possible.