Skip to content

Commit 4a44ee1

Browse files
committed
New RFC: Collection Transmute
1 parent 356cb57 commit 4a44ee1

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

text/0000-collection-transmute.md

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
- Feature Name: collection-transmute
2+
- Start Date: 2019-09-05
3+
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
4+
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add a `transmute(self)` method to `Vec`, `VecDeque` and `BinaryHeap`.
10+
11+
# Motivation
12+
[motivation]: #motivation
13+
14+
The mentioned types cannot safely be transmuted by `mem::transmute`. Adding a
15+
method for that purpose will hopefully discourage users to try anyway.
16+
17+
E.g. the following code is UB:
18+
19+
```rust
20+
let x = vec![0u32; 2];
21+
let y = unsafe { std::mem::transmute::<_, Vec<[u8; 4]>>(x) };
22+
```
23+
24+
This is explained in the docs for [`Vec`], but with an unsound solution. The
25+
way to do this correctly turns out surprisingly tricky:
26+
27+
```rust
28+
let x = vec![0u32; 2];
29+
let y = unsafe {
30+
let y: &mut Vec<_> = &mut *ManuallyDrop::new(x);
31+
Vec::from_raw_parts(y.as_mut_ptr() as *mut [u8; 4],
32+
y.len(),
33+
y.capacity())
34+
};
35+
```
36+
37+
Though the code is not too large, there are a good number of things to get
38+
wrong – this solution was iteratively created by soundness-knowledgeable
39+
Rustaceans, with multiple wrong attempts. So this method seems like a good
40+
candidate for inclusion into `std`.
41+
42+
This also applies to `VecDeque` and `BinaryHeap`, which are implemented in
43+
terms of `Vec`.
44+
45+
[`Vec`]: TODO
46+
47+
# Guide-level explanation
48+
[guide-level-explanation]: #guide-level-explanation
49+
50+
The types `std::vec::Vec`, `std::collections::VecDeque` and
51+
`std::collections::BinaryHeap` all get a new unsafe `transmute` method that
52+
takes `self` by value and returns a new `Vec`/`VecDeque`/`BinaryHeap` with a
53+
caller-chosen item type.
54+
55+
The API might look like this (exemplified for `Vec`):
56+
57+
```rust
58+
impl<T> Vec<T> {
59+
/// Transmute this `Vec` to a different item type
60+
///
61+
/// # Safety
62+
///
63+
/// Calling this function requires the target item type be compatible with
64+
/// `Self::Item` (see [`mem::transmute`]).
65+
///
66+
/// # Examples
67+
///
68+
/// transmute a `Vec` of 32-bit integers to their byte representations:
69+
///
70+
/// ```
71+
/// let x = vec![0u32; 5];
72+
/// let y = unsafe { x.transmute::<[u8; 4]>() };
73+
/// assert_eq!(5, y.len());
74+
/// assert_eq!([0, 0, 0, 0], y[0]);
75+
/// ```
76+
///
77+
/// [`mem::transmute`]: ../../std/mem/fn.transmute.html
78+
unsafe fn transmute<I>(self) -> Vec<I> {
79+
..
80+
}
81+
```
82+
83+
This would mean our example above would become:
84+
85+
```rust
86+
let x = vec![0i32; 2];
87+
let y = x.transmute::<[u8; 4]>();
88+
```
89+
90+
The documentation of `mem::transmute` should link to the new methods.
91+
92+
A clippy lint can catch offending calls to `mem::transmute` and suggest using
93+
the inherent `transmute` method where applicable.
94+
95+
# Reference-level explanation
96+
[reference-level-explanation]: #reference-level-explanation
97+
98+
The implementation for `Vec` copies the above solution. The methods for
99+
`VecDeque` and `BinaryHeap` use the `Vec` method on their data.
100+
101+
# Drawbacks
102+
[drawbacks]: #drawbacks
103+
104+
Adding a new method to `std` increases code size and needs to be maintained.
105+
However, this seems to be a minor inconvenience when compared to the safety
106+
benefit.
107+
108+
# Rationale and alternatives
109+
[rationale-and-alternatives]: #rationale-and-alternatives
110+
111+
As explained above, the method is useful, yet non-obvious. There were multiple
112+
wrong implementation attempts before that were not chosen for being unsound.
113+
114+
We could do nothing, but this means users wanting to transmute the items of a
115+
`Vec` will be left without an obvious solution which will lead to unsound code
116+
if they get it wrong.
117+
118+
We could document the correct solution instead of putting it into `std`. This
119+
would lead to worse ergonomics.
120+
121+
We could create a trait to hold the `transmute` method. This would allow more
122+
generic usage, but might lead to worse ergonomics due to type inference
123+
uncertainty.
124+
125+
It would even be possible to provide a default implementation using
126+
`mem::transmute`, but having a default implementation that might be unsound for
127+
some types is a footgun waiting to happen.
128+
129+
# Prior art
130+
[prior-art]: #prior-art
131+
132+
`mem::transmute` offers the same functionality for many other types. We have
133+
added similar methods to different types where useful, see the various
134+
iterator-like methods in `std`. @Shnatsel and @danielhenrymantilla came up
135+
with the solution together in a
136+
[clippy issue](https://github.com/rust-lang/rust-clippy/issues/4484).
137+
138+
# Unresolved questions
139+
[unresolved-questions]: #unresolved-questions
140+
141+
- are there other types that would benefit from such a method? What about
142+
`HashSet`, `HashMap` and `LinkedList`?
143+
- would it make sense to add implementations to types where `mem::transmute` is
144+
acceptable, to steer people away from the latter?
145+
- this RFC does not deal with collection types in crates such as
146+
[`SmallVec`](https://docs.rs/smallvec), though it is likely an implementation
147+
in `std` might motivate the maintainers to include similar methods.
148+
149+
# Future possibilities
150+
[future-possibilities]: #future-possibilities
151+
152+
The author cannot think of anything not already outlined above.

0 commit comments

Comments
 (0)