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 &T
s), 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_mut
and#[enum_ptr(borrow)]
/#[enum_ptr(borrow_mut)]
, implementFieldDeref
/FieldDerefMut
. - Unsatisfied with derived reference types? Implement
CompactBorrow
/CompactBorrowMut
by hand.
§Limitations
Suppose we are deriving from Foo
, then
Foo
must 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
Foo
must have exactly one field.- Unit variants are not allowed due to performance concerns.
- If you need a unit variant, use
Unit
.
- Each variant of
Foo
must 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
,Rc
andArc
support
Macros§
Structs§
- Compact representation of
T
. Only one-pointer wide. isize
that shifts left byN
bits.usize
that shifts left byN
bits.- Placeholder of unit variants.
Traits§
- Types (may not be pointers) that can be used in
EnumPtr
. - Types that can be used by
get_ref
and to deriveCompactBorrow
. - Types that can be used by
get_mut
and to deriveCompactBorrowMut
.
Derive Macros§
- Derives conversions to and from
Compact
.