Bitwise

#249 Jul 2026

249. bit_width — How Many Bits Does This Number Need? (New in Rust 1.97)

“How many bits do I need for this value?” used to mean 32 - n.leading_zeros() — a formula you rewrite every time the integer type changes. Rust 1.97 — hitting stable today — ships bit_width and friends.

The old dance

Bit packing, varint encoding, sizing a field: they all start with the same question — the position of the highest set bit, plus one. The classic answers both have sharp edges:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let n = 300_u32;

// Formula depends on the type's width —
// silently wrong after a switch to u64:
let bits = 32 - n.leading_zeros();

// Reads better, but panics on 0:
let bits2 = n.ilog2() + 1;

assert_eq!(bits, 9);
assert_eq!(bits2, 9);

The leading_zeros version encodes the type width (32) by hand. The ilog2 version blows up on zero, so real code needs an if n == 0 guard around it.

bit_width says what you mean

Stable since Rust 1.97 on all integers:

1
2
3
4
5
assert_eq!(0_u32.bit_width(), 0);
assert_eq!(1_u32.bit_width(), 1);
assert_eq!(255_u32.bit_width(), 8);
assert_eq!(256_u32.bit_width(), 9);
assert_eq!(u32::MAX.bit_width(), 32);

Zero needs zero bits — no panic, no guard. The width of the type is no longer baked into your arithmetic, so the same line keeps working when a refactor turns u32 into u64.

Bonus: isolate_lowest_one kills the wrapping_neg hack

The same release stabilizes isolate_lowest_one and isolate_highest_one, which keep just the lowest or highest set bit. The lowest-bit version replaces the venerable x & x.wrapping_neg() two’s-complement trick — the one you either know or google every time:

1
2
3
4
5
let x = 0b1011000_u32;

assert_eq!(x.isolate_lowest_one(),  0b0001000);
assert_eq!(x.isolate_highest_one(), 0b1000000);
assert_eq!(0_u32.isolate_lowest_one(), 0);

That makes the standard “iterate the set bits” loop readable:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let mut bits = 0b1001_0110_u32;
let mut positions = Vec::new();

while bits != 0 {
    let low = bits.isolate_lowest_one();
    positions.push(low.trailing_zeros());
    bits ^= low; // clear the bit we handled
}

assert_eq!(positions, [1, 2, 4, 7]);

No two’s-complement folklore in sight — the intent is right there in the method names.