Now that offset_from
is a const fn
(behind a feature flag), this can be implemented as a const-friendly macro (I haven't tested this since I nightly hasn't yet been updated with const_ptr_offset_from
; I may revise this in the next day or two when I get a chance to actually test it):
#![feature(const_transmute)]
#![feature(const_ptr_offset_from)]
#![feature(ptr_offset_from)]
macro_rules! offset_of {
($Struct:path, $($field:tt)+) => ({
const OFFSET: usize = {
extern crate core;
let base_uninit = core::mem::MaybeUninit::<$Struct>::uninit();
unsafe {
// This is UB, but it's the best we can do for the time being.
// If this is in `core`, we can just leave a comment to tell
// people that this is an unsafe implementation detail that
// works here in `core` but is UB outside of this macro.
let base_ref = core::mem::transmute::<_, &$Struct>(&base_uninit);
let base_u8_ptr = base_ref as *const _ as *const u8;
let field_u8_ptr = &base_ref.$($field)+ as *const _ as *const u8;
let offset = field_u8_ptr.offset_from(base_u8_ptr) as usize;
// Make sure the offset computation stayed within the struct's size.
let assert = [offset; 1];
assert[(offset <= core::mem::size_of::<$Struct>()) as usize - 1]
}
};
OFFSET
})
}
Using $($field:tt)+
allows this to support offset_of!(Struct, field.sub_field.sub_array[3])
.