Skip to content

doc: improve documentation of packed type layout modifier #318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 52 additions & 13 deletions src/other-reprs.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,26 +119,65 @@ assert_eq!(16, size_of::<MyReprOption<&u16>>());

This optimization still applies to fieldless enums with an explicit `repr(u*)`, `repr(i*)`, or `repr(C)`.

## repr(packed)
## repr(packed(n))

`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
`repr(packed(n))` can lower the alignment of every single struct member and thus
the alignment of the struct itself. The final alignment of each struct member is
`min(n, normal_alignment)`. Hence, `packed`/`packed(1)` aligns each field to a
one-byte boundary. Effectively, this strips any padding between member fields,
which 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
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove this from the PR if you want. This was necessary so that each paragraph has roughly the same width in the markdown file.

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.
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 this can cause undefined behavior][ub loads], the lint has been implemented
and it will become a hard error.
[As this can cause undefined behavior][ub loads], the lint has been implemented.
With Rust 1.57 stable it is still a warning, but will become a hard error in the
future.

`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)`.
This repr is a modifier on `repr(C)` and `repr(Rust)`. A typical use case is
`repr(C, packed)`, which gives you full control over the exact type layout in
memory.

### Example

The example down below shows how you can mutate data in a packed struct and safely
handle unaligned pointers.

```rust
#[derive(Default)]
#[repr(packed)]
struct Foo {
a: u8,
b: u64,
}

impl Foo {
// safe way of creating an unaligned pointer to the field
fn b_mut_ptr(&mut self) -> *mut u64 {
core::ptr::addr_of_mut!(self.b)
}
}

fn main() {
println!("{:?}", {
let mut foo = Foo::default();
let ptr = foo.b_mut_ptr();
unsafe {
// safely write to the unaligned ptr
core::ptr::write_unaligned(ptr, *ptr + 1);
*ptr
}
});
}
```

## repr(align(n))

Expand All @@ -150,7 +189,7 @@ 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)`.
`repr(packed)`, but a struct with `align(n)` can wrap a struct that is `packed`.

[unsafe code guidelines]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html
[drop flags]: drop-flags.html
Expand Down
Loading