Derive Macro musli_zerocopy::ZeroCopy
source · #[derive(ZeroCopy)]
{
// Attributes available to this derive:
#[zero_copy]
}
Expand description
Derive macro to implement ZeroCopy
.
Implementing this trait ensures that the type can safely be coerced to and from initialized bytes.
§Using with structs
The following are the requirements for deriving structs:
- The struct must either be
#[repr(C)]
or[repr(transparent)]
. - All fields in the struct must either implement
ZeroCopy
or beZeroSized
and marked as#[zero_copy(ignore)]
.
If the struct is zero-sized, it will implement ZeroSized
along with the
ZeroCopy
trait.
use musli_zerocopy::{OwnedBuf, ZeroCopy};
#[derive(Debug, PartialEq, ZeroCopy)]
#[repr(C, align(128))]
struct Custom { field: u32 }
let mut buf = OwnedBuf::new();
let ptr = buf.store(&Custom { field: 10 });
buf.align_in_place();
assert_eq!(buf.load(ptr)?, &Custom { field: 10 });
§Using with enums
The following are the requirements for deriving for enums:
The enum must be marked with a valid, fixed representation. Such as
#[repr(u8)]
, or #[repr(usize)]
.
use musli_zerocopy::ZeroCopy;
#[derive(ZeroCopy)]
#[repr(u8)]
enum Flags {
First ,
Second(u32),
Third {
first: u32,
second: u64,
},
}
If a custom discriminant is used, only constant expressions are supported.
For example:
- A literal like
42
, - A simple constant expression like
u32::MIN + 10
. - A more complex constant expression like
my_constant_fn(u32::MIN >> 2)
.
const fn my_constant_fn(base: u8) -> u8 {
base + 3
}
#[derive(ZeroCopy)]
#[repr(u8)]
enum Flags {
First = 1,
Second(u32), // will automatically assigned 2
Third {
first: u32,
second: u64,
} = u8::MAX,
Fourth = my_constant_fn(u8::MIN >> 2),
}
Complete example:
use musli_zerocopy::{OwnedBuf, ZeroCopy};
#[derive(Debug, PartialEq, ZeroCopy)]
#[repr(u8)]
enum Flags {
First,
Second(u32),
Third { first: u32, second: u64 },
}
let mut buf = OwnedBuf::with_alignment::<Flags>();
buf.clear();
let ptr = buf.store(&Flags::First);
assert_eq!(buf.load(ptr)?, &Flags::First);
buf.clear();
let ptr = buf.store(&Flags::Second(42));
assert_eq!(buf.load(ptr)?, &Flags::Second(42));
buf.clear();
let ptr = buf.store(&Flags::Third { first: 42, second: 84 });
assert_eq!(buf.load(ptr)?, &Flags::Third { first: 42, second: 84 });
§Padding
The constant ZeroCopy::Padded
determines whether the derives struct uses
padding or not. This derive currently uses a fairly conservative algorithm:
The constant ZeroCopy::Padded
will be set to true
if:
- The size of the type is 0, and the alignment is larger than 1. This
indicates a zero-sized type with an explicit
#[repr(align(N))]
that is not set to 1. - The sum of the size of all the fields is not the same as the size of the type.
- Any of the fields has its
ZeroCopy::Padded
set totrue
. - For enums, we test every variant with the same rules, except each variant
is treated as a struct where the discriminant (
u32
in#[repr(u32)]
) is treated like [a leading hidden field].
use musli_zerocopy::ZeroCopy;
#[derive(ZeroCopy)]
#[repr(C)]
struct Zst;
const _: () = assert!(!Zst::PADDED);
#[derive(ZeroCopy)]
#[repr(C, align(1))]
struct ZstAlign1;
const _: () = assert!(!ZstAlign1::PADDED);
#[derive(ZeroCopy)]
#[repr(C, align(128))]
struct ZstPadded;
const _: () = assert!(!ZstPadded::PADDED);
#[derive(ZeroCopy)]
#[repr(u8)]
enum ZstEnum { EmptyField }
const _: () = assert!(!ZstEnum::PADDED);
#[derive(ZeroCopy)]
#[repr(u8)]
enum SameEnum { Variant1(u8), Variant2(u8) }
const _: () = assert!(!SameEnum::PADDED);
#[derive(ZeroCopy)]
#[repr(u16)]
enum PaddedU16 { Variant1(u8), Variant2(u8) }
const _: () = assert!(PaddedU16::PADDED);
#[derive(ZeroCopy)]
#[repr(u16)]
enum NotPaddedU16 { Variant1(u8, u8), Variant2([u8; 2]), Variant3(u16) }
const _: () = assert!(!NotPaddedU16::PADDED);
#[derive(ZeroCopy)]
#[repr(C, packed)]
struct Packed { inner: u8, inner2: u32 }
const _: () = assert!(!Packed::PADDED);
#[derive(ZeroCopy)]
#[repr(C, packed(2))]
struct Packed1 { inner: u8, inner2: u32 }
const _: () = assert!(Packed1::PADDED);
#[derive(ZeroCopy)]
#[repr(C)]
struct Inner { inner: u8, inner2: u32 }
const _: () = assert!(Inner::PADDED);
#[derive(ZeroCopy)]
#[repr(C)]
struct Outer { first: u8, inner: Inner }
const _: () = assert!(Outer::PADDED);
§Supported attributes
§Type attributes
The following repr
attributes are supported:
#[repr(C)]
- Ensures that the type has the mandatory represention.#[repr(transparent)]
- If there is a single field inside of the marked struct which implementsZeroCopy
.#[repr(align(N))]
- Allows for control over the type’s alignment.#[repr(packed)]
or#[repr(packed(N))]
- Allows for control over the struct alignment. Namely to lower it. This is not supported for enums.
The following zero_copy(..)
attribute are supported:
§#[zero_copy(bounds = {<bound>,*})]
Allows for adding additional bounds to implement ZeroCopy
for generic
types:
use musli_zerocopy::ZeroCopy;
#[derive(ZeroCopy)]
#[repr(C)]
#[zero_copy(bounds = {A: ZeroCopy, B: ZeroCopy})]
struct Pair<A, B> {
left: A,
right: B,
}
§#[zero_copy(crate = <path>)]
Allows for specifying a custom path to the `musli_zerocopy`` crate (default).
use zerocopy::ZeroCopy;
#[derive(ZeroCopy)]
#[repr(C)]
#[zero_copy(crate = zerocopy)]
struct Custom { field: u32 }