A multi-type container with a static size.
This crate is intended for use in no_std
environments.
Trait objects require dynamic memory allocation since the concrete type being dispatched is not known until runtime.
For no_std
environments, a global allocator may be limited, or not available at all. But dynamic dispatch may still be desired.
This crate provides an attribute macro for generating an enum type that contains a finite set of concrete types.
Each variant of the enum corresponds to a type, in the form of a tuple variant.
Rust enums occupy as much space as the largest variant:
enum MultipleTypes {
A(A),
B(B),
C(C),
D(D)
}
Memory:
|# <- A
|### <- B
|## <- C
|##### <- D
##|##### < Total size
^
|
padding/tag
To create a bundle, simply mark an enum as a such:
trait Foo {
fn bar(&self) -> u8;
}
#[bundle]
enum MyBundle {
FirstType,
SecondType,
ThirdType
}
Now you can invoke methods defined by the shared trait:
impl Foo for FirstType {
fn bar(&self) -> u8 {
0
}
}
impl Foo for SecondType {
fn bar(&self) -> u8 {
1
}
}
impl Foo for ThirdType {
fn bar(&self) -> u8 {
2
}
}
let bundle: MyBundle = { /* fetch bundle from somewhere... */ }
let bar = use_my_bundle!(bundle, |inner| { inner.bar() }); // will be 0, 1, or 2 depending on what's in the bundle
Bundles can still be used with other macros as long as the bundle is the first one executed.
For example, use with derive
where types A
, B
and C
implement Clone
:
#[bundle] // this goes first so derive sees the transformed enum
#[derive(Clone)]
enum MyBundle {
A,
B,
C,
}
If generics are required for your bundle, you can add them like so:
#[bundle]
enum MyBundle<T: Trait1, U: Trait2> {
A(A<T>), // notice you must now create the tuple variant yourself
B,
C(C<U>),
}
The #[bundle]
macro cannot generate unsafe code.