Hey all, I believe I recently had a breakthrough concerning how to think about unsafe code and I tried to prove it to myself by implementing a function that performs a mapping of Vec<T>
to Vec<U>
without allocating (given equal size and alignment of T and U). So here is my code
#![feature(vec_into_raw_parts)]
fn transform_in_place<T,U,F>(v: Vec<T>, mut f: F) -> Vec<U>
where F : FnMut(T) -> U {
assert!(std::mem::align_of::<T>() == std::mem::align_of::<U>());
assert!(std::mem::size_of::<T>() == std::mem::size_of::<U>());
unsafe {
let (pstart,len,cap) = v.into_raw_parts();
for pelem in (0..len).map(|j| pstart.offset(j.try_into().unwrap())) {
let mut t = MaybeUninit::uninit();
core::intrinsics::copy_nonoverlapping(pelem, t.as_mut_ptr(), 1);
let u = f(t.assume_init());
let pu :*mut U = pelem as _;
*pu = u;
}
Vec::from_raw_parts(pstart as _, len, cap)
}
}
So what I do is
- get the raw parts of a vector (pointer to mem, len, capacity)
- iterate over the addresses of the elements in the vector
- create a temporary element of type t into which I copy the vector element without actually dereferencing the element (so T does not have to implement Copy)
- I perform the transformation and put the result into the memory previously occupied by the element
- finally I create a new vector from the raw parts
I believe I understand what I am doing and I did create a test case and run it with miri without complaints. However, I really appreciate a code review that points out what I could do better and / or where I am wrong.
If there is something I can do better in terms of performance, I also appreciate hints
and yes, I know that the standard library is almost guarantee to do what I want if I take the idiomatic approach. But as I said, this is mostly about testing my understanding of unsafe.