Description
Documentation on std::mem::transmute() specifically calls out that transmuting a Vec of any type is UB.
Code with UB from documentation:
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.
let v_transmuted = unsafe {
std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(v_orig)
};
Correct code from documentation (assuming interior types can be transmuted):
let store = [0, 1, 2, 3];
let mut v_orig = store.iter().collect::<Vec<&i32>>();
// Using from_raw_parts
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);
What exactly is wrong with it is not currently documented (I've opened rust-lang/rust#64073 to clarify this) but my understanding is that the problem stems from the following:
Most fundamentally, Vec is and always will be a (pointer, capacity, length) triplet. No more, no less. The order of these fields is completely unspecified, and you should use the appropriate methods to modify these.
I.e. while it's safe to transmute the data on the heap assuming the types are compatible, it's never safe to transmute the (pointer, capacity, length) triplet on the stack.
I'm not 100% convinced that the "correct" code proposed in documentation correctly upholds aliasing invariants. It's possible that std::mem::forget()
must be called before the new Vec is constructed, or that it's only safe to do with ManuallyDrop
.
cc @RalfJung