Могу ли я использовать "оптимизацию нулевого указателя" для собственных типов не указателей?
Когда у вас есть 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
. Возможно ли это?
Ответы
Ответ 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
}