Подтвердить что ты не робот

Могу ли я использовать "оптимизацию нулевого указателя" для собственных типов не указателей?

Когда у вас есть Option<&T>, компилятор знает, что NULL никогда не является возможным значением для &T, а кодирует вариант None как NULL вместо. Это позволяет экономить пространство:

use std::mem;

fn main() {
    assert_eq!(mem::size_of::<&u8>(), mem::size_of::<Option<&u8>>());
}

Однако, если вы делаете то же самое с не указательным типом, дополнительных битов для хранения этого значения не требуется и требуется дополнительное пространство:

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>>()); 
}

В общем, это правильно. Тем не менее, я хотел бы перейти к оптимизации, потому что я знаю, что мой тип имеет определенные невозможные значения. В качестве придуманного примера у меня может быть персонаж игрока с возрастом. Возраст может быть неизвестным, но никогда не будет выше 255:

struct Age(u8);

struct Player {
    age: Option<Age>,
}

Я хотел бы сообщить оптимизатору этого ограничения - Age никогда не может быть 255, поэтому можно использовать этот бит в качестве None. Возможно ли это?

4b9b3361

Ответ 1

Нет, вы не можете... в стабильной ржавчине. Если вы хотите перейти к ночному компилятору, вы можете использовать core::nonzero::NonZero. Это действует как обертка, которая сообщает компилятору, что содержимое никогда не будет содержать буквальный ноль. Кроме того, почему Option<Box<T>> имеет размер указателя.

Вот пример, показывающий, как создать Age, и прочитать его полезную нагрузку.

#![feature(core)]
#![allow(dead_code)]

extern crate core;

use core::nonzero::NonZero;

struct Age(NonZero<u8>);

impl Age {
    pub fn new(age: u8) -> Age {
        if age == 0 { panic!("Age cannot be zero!") }
        Age(unsafe { NonZero::new(age) })
    }

    pub fn age(&self) -> u8 {
        *self.0
    }
}

struct Player {
    age: Option<Age>,
}

fn main() {
    println!("size: {}", std::mem::size_of::<Player>());
    // Output: size: 1
}