Skip to content

Commit ae11a44

Browse files
committed
improve description of packed type layout modifier
1 parent 2d66852 commit ae11a44

File tree

1 file changed

+53
-13
lines changed

1 file changed

+53
-13
lines changed

src/other-reprs.md

+53-13
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,65 @@ assert_eq!(16, size_of::<MyReprOption<&u16>>());
112112

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

115-
## repr(packed)
115+
## repr(packed(n))
116116

117-
`repr(packed)` forces Rust to strip any padding, and only align the type to a
118-
byte. This may improve the memory footprint, but will likely have other negative
117+
`repr(packed(n))` can lower the alignment of every single struct member and thus,
118+
the alignment of the struct itself. The final alignment of each struct member is
119+
`min(n, normal_alignment)`. Hence, `packed`/`packed(1)` aligns each field to a
120+
one-byte boundary. Effectively, this strips any padding between member fields,
121+
which may improve the memory footprint, but will likely have other negative
119122
side-effects.
120123

121-
In particular, most architectures *strongly* prefer values to be aligned. This
122-
may mean the unaligned loads are penalized (x86), or even fault (some ARM
123-
chips). For simple cases like directly loading or storing a packed field, the
124-
compiler might be able to paper over alignment issues with shifts and masks.
125-
However if you take a reference to a packed field, it's unlikely that the
126-
compiler will be able to emit code to avoid an unaligned load.
124+
In particular, most architectures *strongly* prefer values to be aligned. This
125+
may mean the unaligned loads are penalized (x86), or even fault (some ARM chips).
126+
For simple cases like directly loading or storing a packed field, the compiler
127+
might be able to paper over alignment issues with shifts and masks. However, if
128+
you take a reference to a packed field, it's unlikely that the compiler will be
129+
able to emit code to avoid an unaligned load.
127130

128-
[As this can cause undefined behavior][ub loads], the lint has been implemented
129-
and it will become a hard error.
131+
[As this can cause undefined behavior][ub loads], the lint has been implemented.
132+
With Rust 1.57 stable it is still a warning, but will become a hard error in the
133+
future.
130134

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

134-
This repr is a modifier on `repr(C)` and `repr(Rust)`.
138+
This repr is a modifier on `repr(C)` and `repr(Rust)`. A typical use case is
139+
`repr(C, packed)`, which gives you full control over the exact type layout in
140+
memory.
141+
142+
### Example
143+
144+
The example down below shows how you can mutate data in a packed struct and safely
145+
handle unaligned pointers.
146+
147+
```rust
148+
#[derive(Default)]
149+
#[repr(packed)]
150+
struct Foo {
151+
a: u8,
152+
b: u64,
153+
}
154+
155+
impl Foo {
156+
// safe way of creating an unaligned pointer to the field
157+
fn b_mut_ptr(&mut self) -> *mut u64 {
158+
core::ptr::addr_of_mut!(self.b)
159+
}
160+
}
161+
162+
fn main() {
163+
println!("{:?}", {
164+
let mut foo = Foo::default();
165+
let ptr = foo.b_mut_ptr();
166+
unsafe {
167+
// safely write to the unaligned ptr
168+
core::ptr::write_unaligned(ptr, *ptr + 1);
169+
*ptr
170+
}
171+
});
172+
}
173+
```
135174

136175
## repr(align(n))
137176

@@ -143,7 +182,8 @@ never share the same cache line with each other (which may speed up certain
143182
kinds of concurrent code).
144183

145184
This is a modifier on `repr(C)` and `repr(Rust)`. It is incompatible with
146-
`repr(packed)`.
185+
`repr(packed)`, but an struct with `align(n)` can wrap a struct, that is
186+
`packed`.
147187

148188
[unsafe code guidelines]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html
149189
[drop flags]: drop-flags.html

0 commit comments

Comments
 (0)