In general, code that initializes a buffer doesn't need to care whether that buffer is initialized, however, this leads to an issue. Since &mut MaybeUninit<T> allows writing any maybe-uninit value to it, you can't cast &mut T to &mut MaybeUninit<T>, since it would allow you to write an uninit value to a definitely-init location. Thus, in order to have a mutable reference which is maybe-uninit, but cannot be de-initialized, you need a separate type.
Motivating examples or use cases
Although the current std::io::Read cannot be changed, other custom Read types could borrow this approach, allowing something like:
Although this currently would require passing in an initially-uninit buffer, and you can't pass an existing &mut [u8] here.
Similarly, any such code which expects to initialize a buffer, but which will not read from the buffer beforehand, could benefit from a separate type which allows initialization, but not de-initialization.
Solution sketch
Two solutions will be presented, basically depending on how we feel about MaybeUninit<T> where T: ?Sized.
Unsized MaybeUninit version
We could add a type Initializable<'a, T>, which internally holds an &mut MaybeUninit<T> but does not grant actual access to the internal pointer to ensure that it is never de-initialized. It will expose the by-reference methods on the MaybeUninit<T> directly, including ones which initialize the MaybeUninit<T> as an initialized, mutable reference, but will not allow writing directly to the reference with safe code.
No-unsized MaybeUninit version.
We could add a type WriteOnly<T>, which wraps &mut MaybeUninit<T>, &mut [MaybeUninit<T>], &mut [MaybeUninit<T>; N], etc. (e.g. WriteOnly<&mut MaybeUninit<T>>) to provide the same functionality of above. This makes it similar to Pin in the sense that you cannot directly access the underlying reference with safe code.
Purpose
Essentially, offering one of these two types would let you initialize them with either a pre-existing, initialized buffer, or a MaybeUninit buffer, while remaining sound.
Offering these in the standard library would allow eventually using them in standard library APIs, for example, like reading to uninitialized buffers. Since the current shape of the MaybeUninit APIs is still undecided for arrays and slices, this would help solidify those as well and offer a solid way for users to write code that initializes maybe-uninitialized buffers.
Alternatives
The biggest alternative is offering this via a crate, which I had been thinking of writing for a while, but never got to. The reason why I'm proposing an ACP right now is because after looking at my existing code, it's pretty trivial to implement, but the more important decision here is where this code should ultimately lie. If this ACP isn't accepted, the alternative would definitely live on in an external crate.
Links and related work
N/A
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
We think this problem seems worth solving, and the standard library might be the right place to solve it.
We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
Proposal
Problem statement
In general, code that initializes a buffer doesn't need to care whether that buffer is initialized, however, this leads to an issue. Since
&mut MaybeUninit<T>
allows writing any maybe-uninit value to it, you can't cast&mut T
to&mut MaybeUninit<T>
, since it would allow you to write an uninit value to a definitely-init location. Thus, in order to have a mutable reference which is maybe-uninit, but cannot be de-initialized, you need a separate type.Motivating examples or use cases
Although the current
std::io::Read
cannot be changed, other customRead
types could borrow this approach, allowing something like:Although this currently would require passing in an initially-uninit buffer, and you can't pass an existing &mut [u8] here.
Similarly, any such code which expects to initialize a buffer, but which will not read from the buffer beforehand, could benefit from a separate type which allows initialization, but not de-initialization.
Solution sketch
Two solutions will be presented, basically depending on how we feel about
MaybeUninit<T> where T: ?Sized
.Unsized
MaybeUninit
versionWe could add a type
Initializable<'a, T>
, which internally holds an&mut MaybeUninit<T>
but does not grant actual access to the internal pointer to ensure that it is never de-initialized. It will expose the by-reference methods on theMaybeUninit<T>
directly, including ones which initialize theMaybeUninit<T>
as an initialized, mutable reference, but will not allow writing directly to the reference with safe code.No-unsized
MaybeUninit
version.We could add a type
WriteOnly<T>
, which wraps&mut MaybeUninit<T>
,&mut [MaybeUninit<T>]
,&mut [MaybeUninit<T>; N]
, etc. (e.g.WriteOnly<&mut MaybeUninit<T>>
) to provide the same functionality of above. This makes it similar toPin
in the sense that you cannot directly access the underlying reference with safe code.Purpose
Essentially, offering one of these two types would let you initialize them with either a pre-existing, initialized buffer, or a
MaybeUninit
buffer, while remaining sound.Offering these in the standard library would allow eventually using them in standard library APIs, for example, like reading to uninitialized buffers. Since the current shape of the
MaybeUninit
APIs is still undecided for arrays and slices, this would help solidify those as well and offer a solid way for users to write code that initializes maybe-uninitialized buffers.Alternatives
The biggest alternative is offering this via a crate, which I had been thinking of writing for a while, but never got to. The reason why I'm proposing an ACP right now is because after looking at my existing code, it's pretty trivial to implement, but the more important decision here is where this code should ultimately lie. If this ACP isn't accepted, the alternative would definitely live on in an external crate.
Links and related work
N/A
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution: