You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nomicon/src/other-reprs.md

103 lines
4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Alternative representations
Rust allows you to specify alternative data layout strategies from the default.
# 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 C
or C++. Any type you expect to pass through an FFI boundary should have
`repr(C)`, as C is the lingua-franca of the programming world. This is also
necessary to soundly do more elaborate tricks with data layout such as
reinterpreting values as a different type.
However, the interaction with Rust's more exotic data layout features must be
kept 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 if
passed through the FFI boundary.
* ZSTs are still zero-sized, even though this is not a standard behavior in
C, and is explicitly contrary to the behavior of an empty type in C++, which
still consumes a byte of space.
* DST pointers (fat pointers), tuples, and enums with data (that is, non-C-like
enums) are not a concept in C, and as such are never FFI-safe.
* As an exception to the rule on enum with data, `Option<&T>` is
FFI-safe if `*const T` is FFI-safe, and they have the same
representation, using the null-pointer optimization: `Some<&T>` is
represented as the pointer, and `None` is represented as a null
pointer. (This rule applies to any enum defined the same way as
libcore's `Option` type, and using the default repr.)
* Tuple structs are like structs with regards to `repr(C)`, as the only
difference from a struct is that the fields arent named.
* **If the type would have any [drop flags], they will still be added**
* This is equivalent to one of `repr(u*)` (see the next section) for enums. The
chosen size is the default enum size for the target platform's C ABI. Note that
enum representation in C is implementation defined, so this is really a "best
guess". In particular, this may be incorrect when the C code of interest is
compiled with certain flags.
* "C-like" enums with `repr(C)` or `repr(u*)` still may not be set to an
integer value without a corresponding variant, even though this is
permitted behavior in C or C++. It is undefined behavior to (unsafely)
construct an instance of an enum that does not match one of its
variants. (This allows exhaustive matches to continue to be written and
compiled as normal.)
# repr(u*), repr(i*)
These specify the size to make a C-like enum. If the discriminant overflows the
integer it has to fit in, it will produce a compile-time error. You can manually
ask Rust to allow this by setting the overflowing element to explicitly be 0.
However Rust will not allow you to create an enum where two variants have the
same discriminant.
The term "C-like enum" only means that the enum doesn't have data in any
of its variants. A C-like enum without a `repr(u*)` or `repr(C)` is
still a Rust native type, and does not have a stable ABI representation.
Adding a `repr` causes it to be treated exactly like the specified
integer size for ABI purposes.
A non-C-like enum is a Rust type with no guaranteed ABI (even if the
only data is `PhantomData` or something else with zero size).
Adding an explicit `repr` to an enum suppresses the null-pointer
optimization.
These reprs have no effect on a struct.
# repr(packed)
`repr(packed)` forces Rust to strip any padding, and only align the type to a
byte. This may improve the memory footprint, but will likely have other negative
side-effects.
In particular, most architectures *strongly* prefer values to be aligned. This
may mean the unaligned loads are penalized (x86), or even fault (some ARM
chips). For simple cases like directly loading or storing a packed field, the
compiler 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 the
compiler will be able to emit code to avoid an unaligned load.
**[As of Rust 1.0 this can cause undefined behavior.][ub loads]**
`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)`.
[drop flags]: drop-flags.html
[ub loads]: https://github.com/rust-lang/rust/issues/27060