Expand description
This crate provides a custom derive macro EnumPtr to generate bridges
between an enum T and Compact<T> with minimum cost. Compact<T> is
the compact representation of T, and it is only one pointer wide.
This is viable because some types’ low bits are always zeros.
Compact<T> utilizes these bits to store the tag (discriminant).
§Examples
use enum_ptr::{Aligned, Compact, EnumPtr, ShiftUsize, Unit};
#[derive(EnumPtr)]
#[repr(C, usize)] // required
enum Foo<'a, T: Aligned> {
A(T), // supports any `T: Aligned`
B(&'a u64),
C(Unit), // use `Unit` for unit variants
D(ShiftUsize<3>), // you can even use non-pointers
E(Box<i64>),
}
let compact_foo: Compact<_> = Foo::A(&1u64).into();
let original_foo: Foo<_> = compact_foo.into();§Usage
This crate provides multiple flavors of APIs.
§Flavor 1: copy everywhere
If your enum type is Copy (e.g., consists of only &Ts), you can
mark it with #[enum_ptr(copy)]. Each time you need to use it, just copy
and extract it. Easy-peasy!
Due to language limitations, we cannot automatically infer copy.
Click to show examples
use enum_ptr::{Compact, EnumPtr};
#[derive(EnumPtr, Debug, Clone, Copy, PartialEq, Eq)]
#[enum_ptr(copy)] // required
#[repr(C, usize)]
enum Foo<'a, 'b> {
A(&'a i32),
B(&'b u32),
}
let foo: Compact<_> = Foo::A(&1).into();
assert_eq!(foo.extract(), Foo::A(&1));
assert_ne!(foo.extract(), Foo::B(&2));§Flavor 2: get_ref & get_mut
If your enum type is not Copy, and you happens to only have references
to the compact value, you can use get_ref and get_mut to get
references to the object that it points to.
For example, if you hold a compact Box<T>, you can use these APIs to
access &T and &mut T. Since there’s no Box<T> in the memory (but only
its compact form), we cannot create &Box<T> and &mut Box<T>. The target
types are specified by FieldDeref and FieldDerefMut.
Click to show examples
use enum_ptr::{get_mut, get_ref, Compact, EnumPtr};
#[derive(EnumPtr)]
#[repr(C, usize)]
enum Foo {
A(Box<i32>),
B(Box<u32>),
}
let mut foo: Compact<_> = Foo::A(Box::new(1)).into();
assert_eq!(get_ref!(foo, Foo::A), Some(&1));
assert_eq!(get_mut!(foo, Foo::A), Some(&mut 1));§Flavor 3: borrow & borrow_mut
get_ref and get_mut can be troublesome if you want to deal with
multiple variants at together. In that case, you can use
borrow and borrow_mut. They
will return derived reference types that you can match.
Check the documentation of EnumPtr for more details.
Click to show examples
use enum_ptr::{Compact, EnumPtr};
#[derive(EnumPtr, Debug)]
#[enum_ptr(borrow, borrow_mut)] // required
#[repr(C, usize)]
enum Foo {
A(Box<i32>),
B(Option<Box<u32>>),
}
// enum FooRef<'enum_ptr> {
// A(&'enum_ptr i32),
// B(Option<&'enum_ptr u32>),
// }
// enum FooRefMut<'enum_ptr> {
// A(&'enum_ptr mut i32),
// B(Option<&'enum_ptr mut u32>),
// }
let mut foo: Compact<_> = Foo::A(Box::new(1)).into();
match foo.borrow() {
FooRef::A(inner) => assert_eq!(inner, &1),
_ => unreachable!(),
}
match foo.borrow_mut() {
FooRefMut::A(inner) => assert_eq!(inner, &mut 1),
_ => unreachable!(),
}§Flavor 4: map_ref & map_mut (legacy)
map_ref and map_mut will copy
(= core::mem::transmute_copy) temporary objects out of its compact ones,
that core::mem::forget-ed as soon as your closure ends, so that no
destructor is needed to be run. They are important for internal
implementations, but less useful for lib users.
Click to show examples
use enum_ptr::{Compact, EnumPtr};
#[derive(EnumPtr, Debug, PartialEq, Eq)]
#[repr(C, usize)]
enum Foo {
A(Box<i32>),
B(Box<u32>),
}
let mut foo: Compact<_> = Foo::A(Box::new(1)).into();
let result = foo.map_ref(|f| match f {
Foo::A(r) => **r,
_ => unreachable!(),
});
assert_eq!(result, 1);
unsafe {
foo.map_mut(|f| match f {
Foo::A(r) => **r = 2,
_ => unreachable!(),
});
}
assert_eq!(foo.extract(), Foo::A(Box::new(2)));§Extension
Most of the important traits are public. You can implement them for your own types.
- To make your types available in
EnumPtr, implementAligned. - To make your types available in
get_ref/get_mutand#[enum_ptr(borrow)]/#[enum_ptr(borrow_mut)], implementFieldDeref/FieldDerefMut. - Unsatisfied with derived reference types? Implement
CompactBorrow/CompactBorrowMutby hand.
§Limitations
Suppose we are deriving from Foo, then
Foomust have a#[repr(C, usize)].- According to the RFC and the Rust Reference,
#[repr(C, usize)]guarantees the memory layout and discriminant values. Thus, we can safely transmute between two representations.
- According to the RFC and the Rust Reference,
- Each variant of
Foomust have exactly one field.- Unit variants are not allowed due to performance concerns.
- If you need a unit variant, use
Unit.
- Each variant of
Foomust have enough alignment to store the tag.- Currently this crate cannot utilize high bits.
Any violation of these rules will trigger a compilation error.
§Features
alloc(default) —Box,RcandArcsupport
Macros§
Structs§
- Compact
- Compact representation of
T. Only one-pointer wide. - Shift
Isize isizethat shifts left byNbits.- Shift
Usize usizethat shifts left byNbits.- Unit
- Placeholder of unit variants.
Traits§
- Aligned
- Types (may not be pointers) that can be used in
EnumPtr. - Compact
Borrow - Types that can be borrowed from
Compact. Typically derived fromEnumPtr. - Compact
Borrow Mut - Types that can be mutably borrowed from
Compact. Typically derived fromEnumPtr. - Field
Deref - Types that can be used by
get_refand to deriveCompactBorrow. - Field
Deref Mut - Types that can be used by
get_mutand to deriveCompactBorrowMut.