Skip to content

Commit b624dd7

Browse files
committed
Document when Box<T> to Box<U> transmute is sound
We hadn't documented when it's sound to transmute a `Box<T>` to a `Box<U>`. Now that we've documented when `*mut T` and `*mut U` can be assumed to have the same layout, let's use that to document when this transmute is sound.
1 parent d995a5a commit b624dd7

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

src/special-types-and-traits.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,31 @@ r[lang-types.box.fundamental]
2121

2222
<!-- Editor Note: This is nowhere close to an exhaustive list -->
2323

24+
r[lang-types.box.transmute]
25+
For types `T` and `U` such that `*mut T` and `*mut U` have the same layout (per [layout.pointer.parametric]) and a [pointer-to-pointer cast] from `*mut T` to `*mut U` is permitted, transmuting a `Box<T>` to a `Box<U>`, where both boxes use the global allocator, is sound when both of the following hold:
26+
27+
- The pointee has the same [size] and [alignment] whether viewed as `T` or as `U`.
28+
- The pointee is a valid value of `U`.
29+
30+
```rust
31+
# use core::mem::transmute;
32+
let boxed: Box<i8> = Box::new(1);
33+
let addr = (&raw const *boxed).addr();
34+
// SAFETY: `i8` and `u8` have the same size and the same alignment, so
35+
// `boxed`'s allocation is valid for `u8`.
36+
let reboxed: Box<u8> = unsafe { transmute(boxed) };
37+
// The transmute reuses the same allocation.
38+
assert_eq!((&raw const *reboxed).addr(), addr);
39+
assert_eq!(*reboxed, 1);
40+
41+
// The pointee may also be unsized: `[u32]` and `[f32]` share a size and
42+
// an alignment, and every bit pattern is a valid `f32`, so transmuting
43+
// the box preserves the slice's length.
44+
let slice: Box<[u32]> = Box::new([1, 2, 3]);
45+
let floats: Box<[f32]> = unsafe { transmute(slice) };
46+
assert_eq!(floats.len(), 3);
47+
```
48+
2449
r[lang-types.rc]
2550
## `Rc<T>`
2651

@@ -186,6 +211,7 @@ These implicit `Sized` bounds may be relaxed by using the special `?Sized` bound
186211
[`UnwindSafe`]: std::panic::UnwindSafe
187212
[`Unpin`]: std::marker::Unpin
188213

214+
[alignment]: layout.properties.align
189215
[Arrays]: types/array.md
190216
[associated types]: items/associated-items.md#associated-types
191217
[call expressions]: expressions/call-expr.md
@@ -205,6 +231,8 @@ These implicit `Sized` bounds may be relaxed by using the special `?Sized` bound
205231
[moved from]: expr.move.movable-place
206232
[operators]: expressions/operator-expr.md
207233
[orphan rules]: items/implementations.md#trait-implementation-coherence
234+
[pointer-to-pointer cast]: expr.as.pointer
235+
[size]: layout.properties.size
208236
[`static` items]: items/static-items.md
209237
[test functions]: attributes/testing.md#the-test-attribute
210238
[the standard library]: std

0 commit comments

Comments
 (0)