offset_of_val
doesn’t need unsafe
code but is (sadly) limited by the need to know the struct
's name to be able to use pattern-matching to bypass Deref
for field access:
macro_rules! offset_of_val {
($s:expr, $field:ident) => ({
let s: &_ = $s;
let o = (&s.f as *const _ as usize).wrapping_sub(s as *const _ as usize);
// Triple check that we are within `*s` still.
assert!((0..=$crate::mem::size_of_val(s)).contains(&o));
o
})
}
If we require specifying the struct
name, we can recover the pattern-matching:
macro_rules! offset_of_val {
($s:expr, $Struct:path.$field:ident) => ({
let s: &$Struct = $s;
// Use pattern-matching to avoid accidentally going through Deref.
let &$Struct { $field: ref f, .. } = s;
let o = (f as *const _ as usize).wrapping_sub(s as *const _ as usize);
// Triple check that we are within `u` still.
assert!((0..=$crate::mem::size_of_val(&u)).contains(&o));
o
})
}
Then we can rewrite offset_of!
to rely on offset_of_val!
:
macro_rules! offset_of {
($Struct:path.$field:ident) => ({
// Using a separate function to minimize unhygienic hazards
// (e.g. unsafety of #[repr(packed)] field borrows).
// Uncomment `const` when `const fn`s can juggle pointers.
/*const*/ fn offset() -> usize {
let u = $crate::mem::MaybeUninit::<$Struct>::uninitialized();
offset_of_val!(unsafe { &*u.as_ptr() }, $Struct.$field)
}
offset()
})
}