When you have an Option<&T>
, the compiler knows that NULL
is never a possible value for &T
, and encodes the None
variant as NULL
instead. This allows for space-saving:
use std::mem;
fn main() {
assert_eq!(mem::size_of::<&u8>(), mem::size_of::<Option<&u8>>());
}
However, if you do the same with a non-pointer type, there's no extra bits to store that value in and extra space is required:
use std::mem;
fn main() {
// fails because left is 1 and right is 2
assert_eq!(mem::size_of::<u8>(), mem::size_of::<Option<u8>>());
}
In general, this is correct. However, I'd like to opt-in to the optimization because I know that my type has certain impossible values. As a made-up-example, I might have a player character that has an age. The age may be unknown, but will never be as high as 255
:
struct Age(u8);
struct Player {
age: Option<Age>,
}
I'd like to be able to inform the optimizer of this constraint - Age
can never be 255
, so it's safe to use that bit pattern as None
. Is this possible?
As of Rust 1.28, you can use std::num::NonZeroU8
(and friends). This acts as a wrapper that tells the compiler the contents of a number will never contain a literal zero. It's also why Option<Box<T>>
is pointer-sized.
Here's an example showing how to create an Age
and read its payload.
use std::num::NonZeroU8; struct Age(NonZeroU8); impl Age { pub fn new(age: u8) -> Age { let age = NonZeroU8::new(age).expect("Age cannot be zero!"); Age(age) } pub fn age(&self) -> u8 { self.0.get() } } struct Player { age: Option<Age>, } fn main() { println!("size: {}", std::mem::size_of::<Player>()); // Output: size: 1 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With