Can I use the "null pointer optimization" for my own non-pointer types?

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
}