(我指的是装箱作为一种在运行时区分整数和指针的方法。一种技术使用了一些编程语言来辅助GC,例如OCaml。不是Rust Box<T>
类型。)>
我有一个看起来像这样的Rust枚举:
#[derive(Clone, Copy, Debug, PartialEq)]
enum Type<'ts> {
TVar(usize),
Constructed(&'ts ConstructedType<'ts>),
}
据我了解,此枚举的内存布局将是两个字。一个用于标签,另一个用于有效负载。如果可能的话,我想用一个词来表达记忆。
类似于OCaml的语言使用一种称为“整数装箱”的技术,该技术利用了指针对齐的事实。这意味着最低位将为0。如果将整数中的位向左移动一个空格,并将整数的最低位设置为1,则您将以该位为标记使用,而代价是整数精度。
Rust指针是否保证对齐?如何在Rust中为我的类型实现此技术?
答案 0 :(得分:4)
我可能不会关注您所说的所有内容,但是我认为您需要union
。
#[derive(Clone, Copy, Debug, PartialEq)]
enum Type<'ts> {
TVar(usize),
Constructed(&'ts ConstructedType<'ts>),
}
union CompactType<'ts> {
num: usize,
ptr: &'ts ConstructedType<'ts>
}
impl<'ts> From<CompactType<'ts>> for Type<'ts> {
fn from(compact: CompactType<'ts>) -> Type<'ts> {
unsafe {
if compact.num & 1 == 1 {
Type::TVar(compact.num >> 1)
} else {
Type::Constructed(compact.ptr)
}
}
}
}
请注意,访问union
的成员是不安全的,并且必须确保所有不变量都得到了强制执行。例如,您必须显式检查CompactType
的正确创建范围内的值,并防止在不进行那些检查的情况下构造对象的可能性。
我建议将构造函数添加到CompactType
中,以防返回Result
或Option
,以防您尝试使用太大的数字或指向类型的指针没有正确对齐。 TryFrom
feature稳定后,您可以使用它,但与此同时:
enum CompactConvertError {
NumTooBig(String),
PtrNotAligned(String),
}
impl<'ts> Type<'ts> {
fn to_compact(&self) -> Result<CompactType<'ts>, CompactConvertError> {
match self {
Type::TVar(num) => {
if num >> (mem::size_of::<usize>() * 8 - 1) == 1 {
Err(CompactConvertError::NumTooBig(
String::from("The last bit of the usize cannot be used here"))
)
} else {
Ok(CompactType { num: num << 1 | 1usize })
}
},
Type::Constructed(c) => {
if mem::align_of_val(*c) % 2 == 1 {
Err(CompactConvertError::PtrNotAligned(
String::from("The pointer must be to a type with even alignment"))
)
} else {
Ok(CompactType { ptr: c })
}
}
}
}
}
这应该足够灵活以将ConstructedType
替换为通用类型参数。唯一的限制是,您不应将其从引用更改为拥有的值,否则您将不得不担心正确删除它-对于稳定的Rust中的union
类型而言,尚无法做到。 / p>
关于对齐方式,如果ConstructedType
的大小仅为1个字节,则需要向其添加对齐方式,以确保其仅在偶数字节上,否则Rust可能会选择将其打包得更紧:
#[align(2)]
struct ConstructedType<'ts> {
// ...
}
如果大小大于2个字节,绝对不要添加#[align(2)]
。也许其他人可以提供有关使该零件更坚固的建议。