#标记指针 #枚举 # #特质 #标签 #标记枚举 #声明

标记指针枚举类型

一组结构体、特性和宏,用于实现标记指针

1 个稳定版本

1.0.0 2021年11月10日

#1337Rust 模式

MIT 许可证

20KB
451

标记指针枚举类型

一组结构体、特性和宏,用于实现标记指针。

基本用法

// import macro
use tagged_pointer_as_enum::tagged_enum;

// declare it like a normal enum but inside tagged_enum! macro
tagged_enum! {
    enum E {
        // specify how many bits you want to be used by tag.
        // should be at least log2(count(variants) + 1), but you can set more
        // if you need to keep ABI stable after adding new variants
        bits = 2;

        A(u8),
        B(Box<String>),
        C(i16),
    }
}

// macro generates constructors:
let a = E::A(42_u8);
let b = E::B(Box::new(String::from("foo")));
let c = E::C(300_i16);

// and a helper module with tags
assert_eq!(tags::A, 1);
assert_eq!(tags::B, 2);
assert_eq!(tags::C, 3);

// these tags can be used to check variant of enum
assert_eq!(a.tag(), tags::A);
assert_eq!(b.tag(), tags::B);
assert_eq!(c.tag(), tags::C);

// only variants that behave like containers can be borrowed
assert_eq!(b.borrow_value::<Box<String>, String>(), &String::from("foo"));
// borrowing values variants is impossible
// because there's no memory location containing value WITHOUT tag

// of course, you can get values back
assert_eq!(a.unwrap::<u8>(), 42);
assert_eq!(b.unwrap::<Box<String>>(), Box::new(String::from("foo")));
assert_eq!(c.unwrap::<u16>(), 300);

自定义变体类型

默认情况下,以下类型可以用作变体

  • u8
  • u16
  • u32
  • i8
  • i16
  • i32
  • Box<T>
  • Option<Box<T>>

可以通过为它们实现 TaggedPointerValue 来使用其他类型

use tagged_pointer_as_enum::{tagged_enum, TaggedPointerValue};

struct Custom {
    low: u8,
    high: u8
}

// even if it looks like a marker trait in fact it's not
impl TaggedPointerValue for Custom {}

tagged_enum! {
    enum E {
        bits = 1;

        Custom(Custom),
    }
}

let custom = E::Custom(Custom { low: 1, high: 2 });
let unwrapped = custom.unwrap::<Custom>();
assert_eq!(unwrapped.low, 1);
assert_eq!(unwrapped.high, 2);

实现默认特质

默认情况下,tagged_enum! 宏生成的结构体不实现任何内置特质。

可以像通常附加到原生 Rust 枚举一样附加类似的 #[derive(...)] 元数据,但是所有变体类型也必须实现这些特质。

仅支持以下特质

  • Debug
  • Clone
  • PartialEq
  • Eq

此外,所有标记枚举都会自动实现 Drop

use tagged_pointer_as_enum::{tagged_enum, TaggedPointerValue};

#[derive(Debug)]
struct NumAbsCompare {
    n: i32
}
impl TaggedPointerValue for NumAbsCompare {}

// implement comparison by absolute value
impl PartialEq for NumAbsCompare {
    fn eq(&self, other: &Self) -> bool {
        self.n.abs() == other.n.abs()
    }
}

tagged_enum! {
    #[derive(Debug, PartialEq)]
    enum E {
        bits = 2;

        I32(i32),
        NumAbsCompare(NumAbsCompare),
    }
}

// variants ARE equal if they have the same tag and value
assert_eq!(
    E::NumAbsCompare(NumAbsCompare { n:  100 }),
    E::NumAbsCompare(NumAbsCompare { n: -100 }),
);
// variants ARE NOT equal if tags are different
assert_ne!(
    E::NumAbsCompare(NumAbsCompare { n: 100 }),
    E::I32(100),
);
// variants ARE NOT equal if values are different
assert_ne!(
    E::NumAbsCompare(NumAbsCompare { n: 100 }),
    E::NumAbsCompare(NumAbsCompare { n: 101 }),
);

依赖

~0–11MB
~94K SLoC