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.