Updates to the data layout chapter

* We now have `union` as another composite data type
* The compiler now reorders fields and makes enums a bit smaller in
  certain situations.
* We have repr(align(n)) in addition to packed.
pull/96/head
Michal 'vorner' Vaner 6 years ago
parent 7f7a597b47
commit dd8054bef8
No known key found for this signature in database
GPG Key ID: F700D0C019E4C66F

@ -105,9 +105,12 @@ knowing that it's *statically impossible* for this value to be an `Err`, as
this would require providing a value of type `Void`.
In principle, Rust can do some interesting analyses and optimizations based
on this fact. For instance, `Result<T, Void>` could be represented as just `T`,
because the `Err` case doesn't actually exist. The following *could* also
compile:
on this fact. For instance, `Result<T, Void>` is represented as just `T`,
because the `Err` case doesn't actually exist (strictly speaking, this is only
an optimization that is not guaranteed, so for example transmuting one into the
other is still UB).
The following *could* also compile:
```rust,ignore
enum Void {}
@ -118,8 +121,7 @@ let res: Result<u32, Void> = Ok(0);
let Ok(num) = res;
```
But neither of these tricks work today, so all Void types get you is
the ability to be confident that certain situations are statically impossible.
But this trick doesn't work yet.
One final subtle detail about empty types is that raw pointers to them are
actually valid to construct, but dereferencing them is Undefined Behavior

@ -1,6 +1,7 @@
# Alternative representations
Rust allows you to specify alternative data layout strategies from the default.
There's also the [reference].
@ -50,6 +51,24 @@ compiled as normal.)
# repr(transparent)
This can only be used on structs with a single non-zero-sized field (there may
be additional zero-sized fields). The effect is that the layout and ABI of the
whole 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 the
struct. An example of that is the [`UnsafeCell`], which can be transmuted into
the type it wraps.
Also, passing the struct through FFI where the inner field type is expected on
the other side is allowed. In particular, this is necessary for `struct
Foo(f32)` to have the same ABI as `f32`.
More details are in the [RFC][rfc-transparent].
# repr(u*), repr(i*)
These specify the size to make a field-less enum. If the discriminant overflows
@ -88,12 +107,27 @@ 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]**
**[As of Rust 1.30.0 this still 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)`.
# repr(align(n))
`repr(align(n))` (where `n` is a power of two) forces the type to have an
alignment of *at least* n.
This enables several tricks, like making sure neighboring elements of an array
never share the same cache line with each other (which may speed up certain
kinds of concurrent code).
This is a modifier on `repr(C)` and `repr(rust)`. It is incompatible with
`repr(packed)`.
[reference]: https://github.com/rust-rfcs/unsafe-code-guidelines/tree/master/reference/src/representation
[drop flags]: drop-flags.html
[ub loads]: https://github.com/rust-lang/rust/issues/27060
[`UnsafeCell`]: ../std/cell/struct.UnsafeCell.html
[rfc-transparent]: https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md

@ -20,6 +20,7 @@ Rust gives you the following ways to lay out composite data:
* tuples (anonymous product types)
* arrays (homogeneous product types)
* enums (named sum types -- tagged unions)
* unions (untagged)
An enum is said to be *field-less* if none of its variants have associated data.
@ -38,7 +39,7 @@ struct A {
will be 32-bit aligned on an architecture that aligns these primitives to their
respective sizes. The whole struct will therefore have a size that is a multiple
of 32-bits. It will potentially become:
of 32-bits. It may become:
```rust
struct A {
@ -50,6 +51,17 @@ struct A {
}
```
or maybe:
```rust
struct A {
b: u32,
c: u16,
a: u8,
_pad: u8,
}
```
There is *no indirection* for these types; all data is stored within the struct,
as you would expect in C. However with the exception of arrays (which are
densely packed and in-order), the layout of data is not by default specified in

@ -6,6 +6,7 @@ The only things that are different in Unsafe Rust are that you can:
* Call `unsafe` functions (including C functions, compiler intrinsics, and the raw allocator)
* Implement `unsafe` traits
* Mutate statics
* Access fields of `union`s
That's it. The reason these operations are relegated to Unsafe is that misusing
any of these things will cause the ever dreaded Undefined Behavior. Invoking

Loading…
Cancel
Save