Newtypes hold invariants in Rust
Wrap a primitive so an invalid value cannot be constructed at all.
#[derive(Debug, Clone, Copy, PartialEq)]
struct Accuracy(f64);
impl Accuracy {
fn new(value: f64) -> Option<Accuracy> {
if (0.0..=100.0).contains(&value) {
Some(Accuracy(value))
} else {
None
}
}
fn value(&self) -> f64 {
self.0
}
}
fn grade(a: Accuracy) -> &'static str {
if a.value() >= 97.0 {
"clean"
} else {
"keep practicing"
}
}
fn main() {
let ok = Accuracy::new(98.5).unwrap();
println!("{:?} {}", ok, grade(ok));
println!("{:?}", Accuracy::new(140.0));
}
How it works
- The field is private, so the constructor is the only door.
newreturnsOptionorResultwhen validation can fail.- The type then proves the invariant everywhere it appears.
Keywords and builtins used here
AccuracyNoneOptionSomeelsef64fngradeifimplletmainnewselfstaticstrstructvalue
The run, in numbers
- Lines
- 30
- Characters to type
- 466
- Tokens
- 148
- Three-star pace
- 110 tpm
At the three-star pace of 110 tokens a minute, this run takes about 81 seconds.
Step 2 of 2 in API patterns, step 11 of 12 in Modules, tests & macros.