RULES
- Introduce a type only when it makes an important invalid state harder to represent or removes repeated checks.
- Parse external values into validated types at the boundary when downstream code relies on the invariant; use
TryFromorFromStrfor fallible conversion. - Use a newtype only when values must not be mixed or the wrapper enforces a real invariant. Use an enum for mutually exclusive states.
- Use
Optionfor absence andResultfor failure. Choose typestate only when compile-time prevention materially improves a multi-step API. - Keep ordinary values and configuration strings ordinary when no validation or distinct behavior is needed.
Example
- ✅ Use
NonZeroU16when zero is invalid for the domain value. - ❌ Wrap every
Stringin a newtype when callers need no distinct behavior or validation.
Validation
In the target crate, test valid and rejected inputs and affected call sites; run cargo check and focused tests.
Integration
Start with rust-essentials; pair with ownership-borrowing when a type changes an ownership contract.