Description
The reasons for such a function are the same as for std::mem::uninitialized
. I saw somewhere a discussion about deprecating std::mem::uninitialized
, but I cannot remember where. If this is the case, how to avoid the cost of initializing a huge structure in the heap that will be initialized again in a ffi function call (without writing c code)?
Also, it would be great to have Box::uninitialized_from_value
that take a reference to a value and return a uninitialized boxed value that could store a copy of the original value. This function is useful to implement clone for structures containing unsized types. For example:
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct A<T: ?Sized> {
a: u8,
b: T,
}
#[derive(Debug, Eq, PartialEq)]
struct X {
inner: Box<A<[u32]>>,
}
impl Clone for X {
fn clone(&self) -> X {
unsafe {
let mut inner = Box::uninitialized_from_value(&*self.inner);
inner.a = self.inner.a;
inner.b.copy_from_slice(&self.inner.b);
X { inner: inner }
}
}
}
fn main() {
let x = X {
inner: Box::new(A {
a: 10,
b: [1, 2, 3, 4, 5],
}),
};
let y = x.clone();
assert_eq!(x, y);
}
If instead of [u32]
, we had a generic [T]
we would had to be very careful in the clone implementation, but it would be possible do to so using the stable compiler. The issue is that without Box::uninitialized_from_value
I think it would be impossible to create an efficient implementation for X::clone
(generic over the array item) without using std:heap
and the nightly compiler.
Here is a possible implementation (I'm not sure it is correct):
#![feature(allocator_api)]
use std::ptr;
use std::heap::{Alloc, Heap, Layout};
use std::mem;
trait Uninitialized<T: ?Sized> {
unsafe fn uninitialized_from_value(value: &T) -> Self;
}
impl<T: ?Sized> Uninitialized<T> for Box<T> {
unsafe fn uninitialized_from_value(value: &T) -> Self {
let layout = Layout::for_value(value);
let ptr = Heap.alloc(layout).unwrap_or_else(|e| Heap.oom(e));
// Initialize b with value so b has the right DST extra field if T is ?Sized
let mut b: Box<T> = mem::transmute(value);
// Change the pointer to the newly allocated memory
ptr::write(&mut b as *mut _ as *mut *mut u8, ptr);
b
}
}
This code is based on Rc::allocate_for_ptr
(which is similar to Arc::allocate_for_ptr
). Box::uninitialized_from_value
could be used in Rc::allocate_for_ptr
, which indicates that there are uses cases for such a function.