[Pre-RFC] Add a new offset_of macro to core::mem

After some discussion with various Rust team members, this is the best that we could come up with as a pure macro solution:

/// Macro to get the offset of a struct field in bytes from the address of the
/// struct.
#[macro_export]
#[allow_internal_unstable]
macro_rules! offset_of {
    ($container:path, $field:ident) => {{
        // Create an instance of the container and calculate the offset to its
        // field. Although we are creating references to uninitialized data this
        // is fine since we are not dereferencing them.
        let val = $crate::__core::mem::MaybeUninit::<$container>::uninitialized();
        let &$container { $field: ref f, .. } = &*val.as_ptr();
        #[allow(unused_unsafe)]
        let result = unsafe { (f as *const _ as *const u8).offset_from(val.as_ptr() as *const u8) };
        result as isize
    }};
}

This code should not have any UB. However it only supports full structs (no tuple structs, no tuples, no arrays) and only supports a single field (no field1.field2).

Considering these restrictions, we feel that a built-in compiler feature is the best way forward. This will allow us to support all types of structs and nested fields, as well as producing a constant value that can be used with const-eval (just like mem::size_of).

The remaining question is whether we want this as a keyword or as a macro-like construct. I believe that allowing an offsetof(Struct, field) in an expression context will be confusing since offsetof is not a real function despite looking like one.

This situation is exactly what the macro syntax was intended for. Consider the exclamation mark is println!(...) which clearly indicates that println is not a normal function. Therefore I believe that offset_of!(Struct, field) is the best way to expose this feature.

2 Likes