如何创建一个Rust类型,可以容纳单个单词大小内的整数或指针?

时间:2018-09-08 06:59:18

标签: pointers optimization rust ocaml union-types

(我指的是装箱作为一种在运行时区分整数和指针的方法。一种技术使用了一些编程语言来辅助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中为我的类型实现此技术?

1 个答案:

答案 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中,以防返回ResultOption,以防您尝试使用太大的数字或指向类型的指针没有正确对齐。 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)]。也许其他人可以提供有关使该零件更坚固的建议。