How can I get the offset of a field in a repr(C) struct as a constant?

Here's a minimal example of what I'm trying to do:

#[repr(C)]
pub struct Example {
    foo: u32,
    bar: usize,
}
impl Example {
    /// The byte offset of `Example::bar`
    const BAR_OFFSET: usize = {
        // what goes here?
    };
}

I could use size_of::<u32>() and align_of::<usize>() to manually calculate the offset but that feels brittle. If it weren't a const I could construct a dummy value and do pointer arithmetic. Are there better options?

Use memoffset crate

That looks great but it says it can't be used as a const?

edit: nevermind…

edit2: you need to enable the "unstable_const" feature on the memoffset crate.

FWIW, although ::memoffset is currently the best effort to get the offset for #[repr(C)] structs that come from external crates, its implementation is technically UB, so if the definition comes from your own crate, you should take advantage of that to get offsets without unsafe and thus without UB:

#![forbid(unsafe_code)]

// ...

with_offsets! {
    #[repr(C)]
    pub
    struct Example {
        #[offset(FOO_OFFSET)]
        foo: u32,

        #[offset(BAR_OFFSET)]
        bar: usize,
    }
}

fn main ()
{
    dbg!(Example::BAR_OFFSET); // outputs 8 on the Playground
}

which, with #[macro_rules_attribute], leads to:

#[macro_use]
extern crate macro_rules_attribute;

#[macro_rules_attribute(with_offsets!)]
#[repr(C)]
pub
struct Example {
    #[offset(FOO_OFFSET)]
    foo: u32,

    #[offset(BAR_OFFSET)]
    bar: usize,
}

EDIT: and here is a version not requiring special identifiers for the constants:

#![forbid(unsafe_code)]

// ...

with_offsets! {
    #[repr(C)]
    pub
    struct Example {
        foo: u32,
        _pad: u8,
        baz: u8,
        bar: u64,
    }
}

fn main ()
{
    assert_eq!(0, dbg!(Example::offset_to.foo));
    assert_eq!(5, dbg!(Example::offset_to.baz));
    assert_eq!(8, dbg!(Example::offset_to.bar));
}
4 Likes

That's exactly what I'm looking for, thanks!

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.