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).