Macro bytemuck::offset_of

source ·
macro_rules! offset_of {
    ($instance:expr, $Type:path, $field:tt) => { ... };
    ($Type:path, $field:tt) => { ... };
}
Expand description

Find the offset in bytes of the given $field of $Type. Requires an already initialized $instance value to work with.

This is similar to the macro from memoffset, however it uses no unsafe code.

This macro has a 3-argument and 2-argument version.

  • In the 3-arg version you specify an instance of the type, the type itself, and the field name.
  • In the 2-arg version the macro will call the default method to make a temporary instance of the type for you.

The output of this macro is the byte offset of the field (as a usize). The calculations of the macro are fixed across the entire program, but if the type used is repr(Rust) then they’re not fixed across compilations or compilers.

Examples

3-arg Usage

// enums can't derive default, and for this example we don't pick one
enum MyExampleEnum {
  A,
  B,
  C,
}

// so now our struct here doesn't have Default
#[repr(C)]
struct MyNotDefaultType {
  pub counter: i32,
  pub some_field: MyExampleEnum,
}

// but we provide an instance of the type and it's all good.
let val = MyNotDefaultType { counter: 5, some_field: MyExampleEnum::A };
assert_eq!(offset_of!(val, MyNotDefaultType, some_field), 4);

2-arg Usage

#[derive(Default)]
#[repr(C)]
struct Vertex {
  pub loc: [f32; 3],
  pub color: [f32; 3],
}
// if the type impls Default the macro can make its own default instance.
assert_eq!(offset_of!(Vertex, loc), 0);
assert_eq!(offset_of!(Vertex, color), 12);

Usage with #[repr(packed)] structs

Attempting to compute the offset of a #[repr(packed)] struct with bytemuck::offset_of! requires an unsafe block. We hope to relax this in the future, but currently it is required to work around a soundness hole in Rust (See rust-lang/rust#27060).

Warning: This is only true for versions of bytemuck > 1.4.0. Previous versions of bytemuck::offset_of! will only emit a warning when used on the field of a packed struct in safe code, which can lead to unsoundness.

For example, the following will fail to compile:

#[repr(C, packed)]
#[derive(Default)]
struct Example {
  field: u32,
}
// Doesn't compile:
let _offset = bytemuck::offset_of!(Example, field);

While the error message this generates will mention the safe_packed_borrows lint, the macro will still fail to compile even if that lint is #[allow]ed:

// Still doesn't compile:
#[allow(safe_packed_borrows)]
{
  let _offset = bytemuck::offset_of!(Example, field);
}

This can be worked around by using unsafe, but it is only sound to do so if you can guarantee that taking a reference to the field is sound.

In practice, this means it only works for fields of align(1) types, or if you know the field’s offset in advance (defeating the point of offset_of) and can prove that the struct’s alignment and the field’s offset are enough to prove the field’s alignment.

Once the raw_ref macros are available, a future version of this crate will use them to lift the limitations of packed structs. For the duration of the 1.x version of this crate that will be behind an on-by-default cargo feature (to maintain minimum rust version support).