Closed
Description
Documentation on mem::transmute presents the following piece of code:
let store = [0, 1, 2, 3];
let mut v_orig = store.iter().collect::<Vec<&i32>>();
// Using transmute: this is Undefined Behavior, and a bad idea.
// However, it is no-copy.
let v_transmuted = unsafe {
std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(
v_orig.clone())
};
// This is the suggested, safe way.
// It does copy the entire vector, though, into a new array.
let v_collected = v_orig.clone()
.into_iter()
.map(|r| Some(r))
.collect::<Vec<Option<&i32>>>();
// The no-copy, unsafe way, still using transmute, but not UB.
// This is equivalent to the original, but safer, and reuses the
// same `Vec` internals. Therefore, the new inner type must have the
// exact same size, and the same alignment, as the old type.
// The same caveats exist for this method as transmute, for
// the original inner type (`&i32`) to the converted inner type
// (`Option<&i32>`), so read the nomicon pages linked above.
let v_from_raw = unsafe {
Vec::from_raw_parts(v_orig.as_mut_ptr() as *mut Option<&i32>,
v_orig.len(),
v_orig.capacity())
};
std::mem::forget(v_orig);
The problem is it's never explained why the first described solution is Undefined Behavior. This is confusing.
Second, the use of transmute from This is actually guaranteed to be safe, but that's not explicitly called out in the snippet. Perhaps transmuting &i32
to Option<&i32>
which is inherently unsafe is not very illustrative.NonZeroU8
to u8
or possibly char
to u32
would be a better example.
Finally, the proposed "correct" way of doing this seems to actually contain undefined behavior: std::mem::forget(v_orig);
is called only after the new vector is created, and until it's called there are two mutable references to the backing storage of v_orig
.