#[repr(C)]
is not a preprocessor directive, since Rust doesn't use a preprocessor 1. It is an attribute. Rust doesn't have a complete specification, but the repr
attribute is mentioned in the Rust reference, so it is absolutely a part of the language. Implementation-wise, attributes are parsed the same way all other Rust code is, and are stored in the same AST. Rust has no "attribute pass": attributes are an actual part of the language. If someone else were to implement a Rust compiler, they would need to implement #[repr(C)]
.
Furthermore, #[repr(C)]
can't be implemented without some compiler magic. In the absence of a #[repr(...)]
, Rust compilers are free to arrange the fields of a struct
/enum
however they want to (and they do take advantage of this for optimization purposes!).
Rust does have a good reason for using it's own memory layout. If compilers aren't tied to how a struct
is written in the source code, they can do optimisations like not storing struct
fields that are never read from, reordering fields for better performance, enum
tag pooling2, and using spare bits throughout NonZero*
s in the struct to store data (the last one isn't happening yet, but might in the future). But the main reason is that Rust has things that just don't make sense in C. For instance, Rust has zero-sized types (like ()
and [i8; 0]
) which can't exist in C, trait
vtables, enum
s with fields, generic types, all of which cause problems when trying to translate them to C.
1 Okay, you could use the C preprocessor with Rust if you really wanted to. Please don't.
2 For example, enum Food { Apple, Pizza(Topping) } enum Topping { Pineapple, Mushroom, Garlic }
can be stored in just 1 byte since there are only 4 possible Food
values that can be created.