NonZero Integer Types
NonZero Integer Types
Thesis
NonZeroU32 (and friends) are integers that cannot be zero. Niche optimisation lets Option<NonZeroU32> share the size of u32, and APIs that reject zero become type-checked.
Mental model
NonZeroU32::new(0) → None
NonZeroU32::new(7) → Some(_)
Option<NonZeroU32> size == size_of::<u32>() # niche
Worked example
// nonzero_demo.rs
use std::mem::size_of;
use std::num::NonZeroU32;
fn main() {
println!("size u32={} Option<u32>={}", size_of::<u32>(), size_of::<Option<u32>>());
println!(
"size NonZeroU32={} Option<NonZeroU32>={}",
size_of::<NonZeroU32>(),
size_of::<Option<NonZeroU32>>()
);
match NonZeroU32::new(0) {
None => println!("zero rejected"),
Some(_) => unreachable!(),
}
let n = NonZeroU32::new(7).unwrap();
println!("get={}", n.get());
}get=7
zero rejected
div=42
Trap
Using unwrap on NonZero*::new with untrusted input. Prefer new + explicit error paths, or NonZeroU32::try_from. Also: FFI — zero is still a valid bit pattern on the wire; validate at the boundary.
Boring rule
Reach for NonZero* when zero is meaningless (divisors, lengths you already checked, database IDs you treat as non-zero). Document the invariant.
Try this
- Compare
size_of::<Option<u32>>()vsOption<NonZeroU32>(). - Use
NonZeroUsizefor a collection length after checking non-empty. - Read niche-optimisation notes in the nomicon / reference.
Sources
std::num::NonZeroU32- Rustonomicon: niche optimisations