Skip to content

Tracking issue for uninitialized constructors for Box, Rc, Arc #63291

Closed
@SimonSapin

Description

@SimonSapin
Contributor

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 than Box<T>. (And similarly for other constructors.) However this would make a call like Box::<u32>::new_uninit() becomes Box::<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.

Activity

added
T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.
B-unstableBlocker: Implemented in the nightly compiler and unstable.
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
on Aug 5, 2019
TimDiekmann

TimDiekmann commented on Oct 5, 2019

@TimDiekmann
Member

Just from looking at the current source code:

pub fn new_uninit() -> Box<mem::MaybeUninit<T>> {
    let layout = alloc::Layout::new::<mem::MaybeUninit<T>>();
    let ptr = unsafe {
        Global.alloc(layout)
            .unwrap_or_else(|_| alloc::handle_alloc_error(layout))
    };
    Box(ptr.cast().into())
}

When mem::size_of::<T>() == 0, a zero-sized layout is passed to Global.alloc but alloc mentions:

This function is unsafe because undefined behavior can result if the caller does not ensure that layout has non-zero size.

Did I have overseen something or is this a bug?

SimonSapin

SimonSapin commented on Oct 5, 2019

@SimonSapin
ContributorAuthor

Good point! I copied this pattern from Rc and Arc, but there the header (refcounts) cause the allocation to never be zero-size.

Box::new_uninit_slice has a similar bug with size_of() == 0 or len == 0.

TimDiekmann

TimDiekmann commented on Oct 5, 2019

@TimDiekmann
Member

I have noticed this when implementing Box for custom allocators with non-zero layouts. Turns out, that this is indeed useful 🙂

This is my implementation for the sliced version.

added a commit that references this issue on Oct 6, 2019

159 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-raw-pointersArea: raw pointers, MaybeUninit, NonNullB-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCLibs-TrackedLibs issues that are tracked on the team's project board.T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.disposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.requires-nightlyThis issue requires a nightly compiler in some way.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @kornelski@joshtriplett@Amanieu@SimonSapin@m-ou-se

      Issue actions

        Tracking issue for uninitialized constructors for Box, Rc, Arc · Issue #63291 · rust-lang/rust