250. lowest_one / highest_one — Set-Bit Positions as an Option, Not a Sentinel
trailing_zeros() on 0 returns the type’s width — a sentinel you must remember to special-case. Rust 1.97 adds lowest_one and highest_one, which return an Option and make “no bits set” impossible to forget.
The sentinel problem
This morning’s bite 249 covered bit_width from today’s Rust 1.97 release. The same release fixes another sharp edge: finding the position of a set bit.
The classic tools each handle “no bits set” badly:
| |
For the highest bit it’s worse: ilog2() panics on zero, and the leading_zeros arithmetic bakes the type width into your formula — the same trap bite 249 described.
An Option is honest
Stable since Rust 1.97 on all integer types:
| |
No sentinel, no panic. The compiler forces you to decide what “no bits” means for your code, instead of letting 8 masquerade as a bit position:
| |
The 1.97 bit family
Together with bite 249, the release completes a tidy family: bit_width (how many bits a value needs), isolate_lowest_one / isolate_highest_one (the bit as a mask), and lowest_one / highest_one (the bit as a position). They agree with each other, too:
| |
If you still write x & x.wrapping_neg() or 31 - n.leading_zeros() from muscle memory, Rust 1.97 is your cue to stop.