我可以对我自己的非指针类型使用"空指针优化"吗?

She*_*ter 28 optimization rust

当你有一个时Option<&T>,编译器就知道它NULL永远不是一个可能的值&T,而是None变量编码为NULL.这样可以节省空间:

use std::mem;

fn main() {
    assert_eq!(mem::size_of::<&u8>(), mem::size_of::<Option<&u8>>());
}
Run Code Online (Sandbox Code Playgroud)

但是,如果使用非指针类型执行相同操作,则不会有额外的位来存储该值,并且需要额外的空间:

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>>()); 
}
Run Code Online (Sandbox Code Playgroud)

一般来说,这是正确的.但是,我想选择加入优化,因为我知道我的类型有某些不可能的值.作为一个例子,我可能有一个有年龄的玩家角色.年龄可能未知,但永远不会高255:

struct Age(u8);

struct Player {
    age: Option<Age>,
}
Run Code Online (Sandbox Code Playgroud)

我希望能够通知优化器这个约束 - Age永远不会255,因此使用该位模式是安全的None.这可能吗?

DK.*_*DK. 21

从Rust 1.28开始,您可以使用std::num::NonZeroU8(和朋友).它充当一个包装器,告诉编译器数字的内容永远不会包含文字零.这也是Option<Box<T>>指针大小的原因.

这是一个示例,说明如何创建Age和读取其有效负载.

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
}
Run Code Online (Sandbox Code Playgroud)

  • 顾名思义,我假设`NonZero` 不允许零值。零以外的值呢?在我的示例中,零可能有效,但 255 无效。 (3认同)
  • @MatthieuM.当然,我只需要做更多的分析,看看它是否真的值得.使用较少的字节似乎是一个明显的胜利; 使用更少的字节*和*强制数学在所有地方都不太可靠. (3认同)
  • @Shepmaster:它被硬编码为非0,我想有一个特征和相关的常数可以扩展这个...但是现在你将不得不接受数学.如果255是你的神奇值,那么应用一个"+ 1"进入存储和"-1"来自存储(使用包装算术)就足以让'NonZero`神奇工作了:) (2认同)
  • @Shepmaster:当然可以; 虽然0是经常使用的魔术值,但是很多分支指令都是专门用于它的(jz`和`jnz`),所以目前还不清楚相关的常数是否会超过它. (2认同)
  • BTW`NonZeroU*`将稳定在1.28. (2认同)