Alternative representations

Rust allows you to specify alternative data layout strategies from the default.There’s also the reference.

repr(C)

This is the most important repr. It has fairly simple intent: do what C does.The order, size, and alignment of fields is exactly what you would expect from Cor C++. Any type you expect to pass through an FFI boundary should haverepr(C), as C is the lingua-franca of the programming world. This is alsonecessary to soundly do more elaborate tricks with data layout such asreinterpreting values as a different type.

We strongly recommend using rust-bindgen and/or cbindgen to manage your FFIboundaries for you. The Rust team works closely with those projects to ensurethat they work robustly and are compatible with current and future guaranteesabout type layouts and reprs.

The interaction of repr(C) with Rust’s more exotic data layout features must bekept in mind. Due to its dual purpose as “for FFI” and “for layout control”,repr(C) can be applied to types that will be nonsensical or problematic ifpassed through the FFI boundary.

  • ZSTs are still zero-sized, even though this is not a standard behavior inC, and is explicitly contrary to the behavior of an empty type in C++, whichsays they should still consume a byte of space.

  • DST pointers (wide pointers) and tuples are not a conceptin C, and as such are never FFI-safe.

  • Enums with fields also aren’t a concept in C or C++, but a valid bridgingof the types is defined.

  • If T is an FFI-safe non-nullable pointertype,Option<T> is guaranteed to have the same layout and ABI as T and istherefore also FFI-safe. As of this writing, this covers &, &mut,and function pointers, all of which can never be null.

  • Tuple structs are like structs with regards to repr(C), as the onlydifference from a struct is that the fields aren’t named.

  • repr(C) is equivalent to one of repr(u*) (see the next section) forfieldless enums. The chosen size is the default enum size for the target platform’s Capplication binary interface (ABI). Note that enum representation in C is implementationdefined, so this is really a “best guess”. In particular, this may be incorrectwhen the C code of interest is compiled with certain flags.

  • Fieldless enums with repr(C) or repr(u*) still may not be set to aninteger value without a corresponding variant, even though this ispermitted behavior in C or C++. It is undefined behavior to (unsafely)construct an instance of an enum that does not match one of itsvariants. (This allows exhaustive matches to continue to be written andcompiled as normal.)

repr(transparent)

This can only be used on structs with a single non-zero-sized field (there maybe additional zero-sized fields). The effect is that the layout and ABI of thewhole struct is guaranteed to be the same as that one field.

The goal is to make it possible to transmute between the single field and thestruct. An example of that is UnsafeCell, which can be transmuted intothe type it wraps.

Also, passing the struct through FFI where the inner field type is expected onthe other side is guaranteed to work. In particular, this is necessary for struct Foo(f32) to always have the same ABI as f32.

More details are in the RFC.

repr(u), repr(i)

These specify the size to make a fieldless enum. If the discriminant overflowsthe integer it has to fit in, it will produce a compile-time error. You canmanually ask Rust to allow this by setting the overflowing element to explicitlybe 0. However Rust will not allow you to create an enum where two variants havethe same discriminant.

The term “fieldless enum” only means that the enum doesn’t have data in anyof its variants. A fieldless enum without a repr(u*) or repr(C) isstill a Rust native type, and does not have a stable ABI representation.Adding a repr causes it to be treated exactly like the specifiedinteger size for ABI purposes.

If the enum has fields, the effect is similar to the effect of repr(C)in that there is a defined layout of the type. This makes it possible topass the enum to C code, or access the type’s raw representation and directlymanipulate its tag and fields. See the RFC for details.

Adding an explicit repr to an enum suppresses the null-pointeroptimization.

These reprs have no effect on a struct.

repr(packed)

repr(packed) forces Rust to strip any padding, and only align the type to abyte. This may improve the memory footprint, but will likely have other negativeside-effects.

In particular, most architectures strongly prefer values to be aligned. Thismay mean the unaligned loads are penalized (x86), or even fault (some ARMchips). For simple cases like directly loading or storing a packed field, thecompiler might be able to paper over alignment issues with shifts and masks.However if you take a reference to a packed field, it’s unlikely that thecompiler will be able to emit code to avoid an unaligned load.

As of Rust 2018, this still can cause undefined behavior.

repr(packed) is not to be used lightly. Unless you have extreme requirements,this should not be used.

This repr is a modifier on repr(C) and repr(rust).

repr(align(n))

repr(align(n)) (where n is a power of two) forces the type to have analignment of at least n.

This enables several tricks, like making sure neighboring elements of an arraynever share the same cache line with each other (which may speed up certainkinds of concurrent code).

This is a modifier on repr(C) and repr(rust). It is incompatible withrepr(packed).