### tagged-pointer-as-enum A set of structs, traits and macros to implement tagged pointers. ### Basic usage ```rust // 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), 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::, 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::(), 42); assert_eq!(b.unwrap::>(), Box::new(String::from("foo"))); assert_eq!(c.unwrap::(), 300); ``` ### Custom variant types By default the following types can be used as variants: + `u8` + `u16` + `u32` + `i8` + `i16` + `i32` + `Box` + `Option>` It is possible to use other types by implementing `TaggedPointerValue` for them: ```rust 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::(); assert_eq!(unwrapped.low, 1); assert_eq!(unwrapped.high, 2); ``` ### Implementing default traits Out of the box `tagged_enum!` macro generates a struct that doesn't implement any builtin traits. It is possible to attach `#[derive(...)]` metadata similar to how it's usually attached to native Rust enums, however all variant types must also implement these traits. **Only the following traits are supported** + `Debug` + `Clone` + `PartialEq` + `Eq` Also, `Drop` is auto-implemented for all tagged enums. ```rust 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 }), ); ```