Crate dyn_struct
source · [−]Expand description
This crate allows you to safely initialize Dynamically Sized Types (DST) using only safe Rust.
#[repr(C)]
#[derive(DynStruct)]
struct MyDynamicType {
pub awesome: bool,
pub number: u32,
pub dynamic: [u32],
}
// the `new` function is generated by the `DynStruct` macro.
let foo: Box<MyDynamicType> = MyDynamicType::new(true, 123, [4, 5, 6, 7]);
assert_eq!(foo.awesome, true);
assert_eq!(foo.number, 123);
assert_eq!(&foo.dynamic, &[4, 5, 6, 7]);Why Dynamic Types?
In Rust, Dynamically Sized Types (DST) are everywhere. Slices ([T]) and trait
objects (dyn Trait) are the most common ones. However, it is also possible
to define your own! For example, this can be done by letting the last field in a
struct be a dynamically sized array (note the missing &):
struct MyDynamicType {
awesome: bool,
number: u32,
dynamic: [u32],
}This tells the Rust compiler that contents of the dynamic-array is laid out in
memory right after the other fields. This can be very preferable in some cases,
since remove one level of indirection and increase cache-locality.
However, there’s a catch! Just as with slices, the compiler does not know how
many elements are in dynamic. Thus, we need what is called a fat-pointer which
stores both a pointer to the actual data, but also the length of the array
itself. As of releasing this crate, the only safe way to construct a dynamic
type is if we know the size of the array at compile-time. However, for most use
cases, that is not possible. Therefore this crate uses some unsafe behind the
scenes to work around the limitations of the language, all wrapped up in a safe
interface.
The Derive Macro
The DynStruct macro can be applied to any #[repr(C)] struct that contains a
dynamically sized array as its last field. Fields only have a single constraint:
they have to implement Copy.
Example
#[repr(C)]
#[derive(DynStruct)]
struct MyDynamicType {
pub awesome: bool,
pub number: u32,
pub dynamic: [u32],
}will produce a single impl-block with a new function. This function accepts all fileds in
the same order they were declared. The last field, however, can be anything implementing
IntoIterator:
impl MyDynamicType {
pub fn new<I>(awesome: bool, number: u32, dynamic: I) -> Box<MyDynamicType>
where I: IntoIterator<Item = u32>,
I::IntoIter: ExactSizeIterator,
{
// ... implementation details ...
}
}Due to the nature of dynamically sized types, the resulting value has to be
built on the heap. For safety reasons we currently only allow returning Box,
though in a future version we may also allow Rc and Arc. In the meantime it
is posible to use Arc::from(MyDynamicType::new(...)).