我正在使用一个小型解释器,我想在堆栈中表示某些类型,而其他类型则作为指针。这是在C++中的样子:

enum {
  NIL_TYPE,
  INT_TYPE,
  REF_TYPE_START,
}

union Data
{
  int int_val;
  void *obj_val
}

struct Object
{
  size_t _type_id;
  Data _data;
}
_type_id充当其余结构的标签。诸如整数, bool 值,nils等之类的东西可以在堆栈上传递,而诸如字符串和对象之类的较大的事物可以通过引用传递。

解释器将在运行时创建新类型,这就是REF_START_TYPE的目的。创建新类型时,我们将向某个内部计数器添加一个值,该值将成为下一个类型ID,并且该类型应为指针。

如何在Rust中表示类似的内容?枚举类型看起来很棒,但是它们似乎不允许扩展。未标记的工会似乎是一个在制品,而没有太多帮助。有什么办法可以得到这种堆栈上的行为(从而减少数学运算期间的大量分配),同时仍然允许运行时扩展?

最佳答案

听起来你想要类似的东西

enum Object {
    Nil,
    Int(i32),
    Runtime(TypeId, RuntimeType),
}

您可以确保RuntimeType仅包含一个指针,或选择立即将其装箱(Runtime(TypeId, Box<RuntimeType>),),但最终结果相同。

如果它包含Box,则此结构在64位计算机上占用24个字节。不幸的是,我无法通知编译器TypeId和枚举的判别式应位于同一位置。如果您的测量结果表明取消引用的程度比额外的堆栈大小小,则可以选择将TypeId移到Box<RuntimeType>中。这取决于所有直接嵌入枚举的其他类型,都是非常具有延展性的。例如,Vec是3指针的堆栈空间。如果包含这些内容,则可以内联更多值。

诀窍变成了:RuntimeType是什么?您对问题的描述不够充分,我无法猜测。它可能是具体类型,或者最终可能是装箱的特征对象。

一个更完整的示例:
struct RuntimeType;
type TypeId = u64;

enum Object {
    Nil,
    Int(i32),
    Runtime(TypeId, RuntimeType),
}

impl Object {
    fn type_id(&self) -> TypeId {
        use Object::*;

        match *self {
            Nil => 0,
            Int(..) => 1,
            Runtime(id, ..) => id,
        }
    }
}

fn main() {}

10-04 14:01