Description
Assigning MaybeUninit::<Foo>::uninit()
to a local variable is usually free, even when size_of::<Foo>()
is large. However, passing it for example to Arc::new
causes at least one copy (from the stack to the newly allocated heap memory) even though there is no meaningful data. It is theoretically possible that a Sufficiently Advanced Compiler could optimize this copy away, but this is reportedly unlikely to happen soon in LLVM.
This issue tracks constructors for containers (Box
, Rc
, Arc
) of MaybeUninit<T>
or [MaybeUninit<T>]
that do not initialized the data, and unsafe conversions to the known-initialized types (without MaybeUninit
). The constructors are guaranteed not to make unnecessary copies.
PR #62451 adds:
impl<T> Box<T> { pub fn new_uninit() -> Box<MaybeUninit<T>> {…} }
impl<T> Box<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Box<T> {…} }
impl<T> Box<[T]> { pub fn new_uninit_slice(len: usize) -> Box<[MaybeUninit<T>]> {…} }
impl<T> Box<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Box<[T]> {…} }
impl<T> Rc<T> { pub fn new_uninit() -> Rc<MaybeUninit<T>> {…} }
impl<T> Rc<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Rc<T> {…} }
impl<T> Rc<[T]> { pub fn new_uninit_slice(len: usize) -> Rc<[MaybeUninit<T>]> {…} }
impl<T> Rc<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Rc<[T]> {…} }
impl<T> Arc<T> { pub fn new_uninit() -> Arc<MaybeUninit<T>> {…} }
impl<T> Arc<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Arc<T> {…} }
impl<T> Arc<[T]> { pub fn new_uninit_slice(len: usize) -> Arc<[MaybeUninit<T>]> {…} }
impl<T> Arc<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Arc<[T]> {…} }
PR #66128 adds:
impl<T> Box<T> { pub fn new_zeroed() -> Box<MaybeUninit<T>> {…} }
impl<T> Arc<T> { pub fn new_zeroed() -> Arc<MaybeUninit<T>> {…} }
impl<T> Rc<T> { pub fn new_zeroed() -> Rc<MaybeUninit<T>> {…} }
Unresolved question:
- The constructor that returns for example
Box<MaybeUninit<T>>
might “belong” more as an associated function of that same type, rather thanBox<T>
. (And similarly for other constructors.) However this would make a call likeBox::<u32>::new_uninit()
becomesBox::<MaybeUnint<u32>>::new_uninit()
which feels unnecessarily verbose. I suspect that this turbofish will be needed in a lot of cases to appease type inference.