Merge pull request #96 from vorner/reprs

Updates to the data layout chapter
pull/104/head
Alexis Beingessner 6 years ago committed by GitHub
commit f8a4e96feb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

@ -1,6 +1,7 @@
# Alternative representations # Alternative representations
Rust allows you to specify alternative data layout strategies from the default. 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*) # repr(u*), repr(i*)
These specify the size to make a field-less enum. If the discriminant overflows 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 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. 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, `repr(packed)` is not to be used lightly. Unless you have extreme requirements,
this should not be used. this should not be used.
This repr is a modifier on `repr(C)` and `repr(rust)`. 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 [drop flags]: drop-flags.html
[ub loads]: https://github.com/rust-lang/rust/issues/27060 [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) * tuples (anonymous product types)
* arrays (homogeneous product types) * arrays (homogeneous product types)
* enums (named sum types -- tagged unions) * enums (named sum types -- tagged unions)
* unions (untagged)
An enum is said to be *field-less* if none of its variants have associated data. 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 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 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 ```rust
struct A { 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, 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 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 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) * Call `unsafe` functions (including C functions, compiler intrinsics, and the raw allocator)
* Implement `unsafe` traits * Implement `unsafe` traits
* Mutate statics * Mutate statics
* Access fields of `union`s
That's it. The reason these operations are relegated to Unsafe is that misusing 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 any of these things will cause the ever dreaded Undefined Behavior. Invoking

Loading…
Cancel
Save