Skip to content

&mut [MaybeUninit<T>]: Fill with value or with closure. #156

Closed
@ajwock

Description

Proposal

Problem statement

This feature would provide more options for making use of slices of uninitialized memory without dropping to unsafe by initializing MaybeUninit slices with closures or a repeated values, akin to slice::fill and slice::fill_with.

Motivation, use-cases

Uninitialized memory is a unique case in terms of read/write safety- it's the only case I can think of where it is safe to write to that memory but unsafe to read from it. In particular, it is safe to treat uninitialized memory as initialized once it has been initialized exhaustively. After calling any method which is guaranteed to initialize all indicies of a MaybeUninit, it is safe to assume_init on that slice.

Over the past couple of years, safe apis for doing MaybeUninit -> T such as write, write_slice, and write_slice_cloned have been introduced. write_slice corresponds to slice::copy_from_slice, while write_slice_cloned corresponds to slice::clone_from_slice. Both are guaranteed to initialize all elements of the slice.

However, there are a couple of other methods which give the same all-initialized guarantee, but there is no corresponding [MaybeUninit] api that safely returns an initialized slice: slice::fill and slice::fill_with.

Another argument for this besides simple write-only slice method API compliance is that, unlike with write_slice/write_slice_cloned, one doesn't need to safely write to initialized memory just so they can safely initialize the uninit memory. The memory can have values or closure results written directly to it without just having to hope LLVM can figure out that the safe write was unnecessary.

Solution sketches

I propose this API:

pub fn write_fill<'a>(this: &'a mut [MaybeUninit<T>], value: T) -> &'a mut [T]
where
    T: Clone

Pretty much the equivalent to slice::fill, but it returns a slice of T at the end.

pub fn write_fill_with<'a, F>(this: &'a mut [MaybeUninit<T>], f: F) -> &'a mut [T]
where
    F: FnMut() -> T

Like slice::fill_with.

Final Thoughts

RFCs suggest to mention potential drawbacks to your approach. One particular drawback that I can think of is MaybeUninit != Uninit, and the implication of this is that calling these methods on a partially initialized slice of MaybeUninit can cause destructors to leak. This is also true of the entire write API that already exists, and is debated on many issues (rust:80376, libs-team:122).

My opinion is that the solution to this is an Uninit api, which contains only the subset of MaybeUninit that guarantees that you either have truly uninitialized memory or you have to give up ownership of the Uninit in exchange for a T. Such an API would include the existing write api as well as these changes.

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

Activity

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

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions