rust
Thinking in Rust
You already know Rust syntax. Change the defaults you reach for first when modeling a domain, handling ownership, designing APIs, or crossing boundaries.
The core failure mode: writing Rust that compiles but thinks like Python, Java, TypeScript, or C. Bare String for domain types. bool for states. Trait objects for closed sets. Error(String) for everything. _ => in every match. Index loops. Sentinel values. Getters and setters on every field. clone() to quiet the compiler. unsafe to escape design pressure. These compile. They are wrong.
Most of these habits come from languages without sum types, ownership, zero-cost newtypes, or exhaustive matching. Recognizing where a pattern comes from helps you see why it is wrong in Rust.
When reviewing Rust, start with the shape of the program: what invariants are represented, who owns each value, which states are impossible, where errors cross boundaries, and whether any escape hatch is hiding a design problem.
Treat these as strong defaults, not rigid laws: when unsure, choose the approach that moves invariants into types and lets the compiler enforce them.