Does repr(C) define a trait I can use to check structs were declared with #repr(C)?


#1

I’m writing a file system where all the structures written to disk must be #repr(C) — does #repr(C) define a trait I can use as a trait bound in my code to enforce this requirement?

Thanks, I’ve been searching around, but don’t see anything.


#2

I don’t think so. The closest thing would be you could provide your own trait and a custom derive:

unsafe trait ReprC {}
#[derive(ReprC)] // expands to unsafe impl ReprC for Dre {}
#[repr(C)]
struct Dre {
    /* ... */
}

which means that in safe code the only way the user could have an implementation of ReprC for their struct is if they go through your derive, and the derive would reject any input that does not have the required #[repr(C)] attribute.


#3

Depending on what your file system is doing with your structs, just having repr(C) may not be the only thing you need. For example I think you can have repr(C) data that contains non-repr(C) data.

#[repr(C)]
struct Dre {
    v: Vec<String>,
}

#4

In that case, is the struct layout of the non-repr(C) content still guaranteed to be the same, or can the compiler reorder it?


#5

Oh, wow. Thanks for the warning.

I just have constant sized arrays and scalars in them. Would be nice to enforce this restriction.


#6

You should provide a trait and custom derive to enforce the things you want.

unsafe trait FlatData {}

unsafe impl FlatData for u8 {}
// ...
#[derive(FlatData)]
#[repr(C)]
struct Dre {
    a: A,
    b: B,
    /* ... */
}

// if the struct has repr(C), expands to:
const IMPL_FLATDATA_FOR_Dre: () = {
    fn assert_flatdata<T: FlatData>() {}
    fn compile_time_checks() {
        assert_flatdata::<A>(); // does not compile if one of the field types is bad
        assert_flatdata::<B>();
        /* ... */
    }

    unsafe impl FlatData for Dre {}
}

#7

The layout of a type is unaffected by the inclusion of that type as a member of another type, such that references to the first type always work as expected.


#8

Ok, so to be sure I get it right, if I include the non-repr(C) struct A in a repr(C) struct B, the compiler may still have optimized the layout of A at compile time, even though it shouldn’t reorder B?


#9

Yes. For example you can also include other crate’s types in your #[repr(C)] type.