Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Meta tracking Issue for MaybeUninit methods for arrays #96097

Open
3 of 5 tasks
clarfonthey opened this issue Apr 16, 2022 · 5 comments
Open
3 of 5 tasks

Meta tracking Issue for MaybeUninit methods for arrays #96097

clarfonthey opened this issue Apr 16, 2022 · 5 comments
Labels
C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@clarfonthey
Copy link
Contributor

clarfonthey commented Apr 16, 2022

This is a meta-tracking issue for multiple APIs that are linked across multiple issues. Right now it only includes two methods, but since there seems to be a desire to add more, this issue can be used as a placeholder for those discussions until those methods are added.

Public API

impl<T> MaybeUninit<T> {
    #[unstable(feature = "maybe_uninit_uninit_array")]
    #[rustc_const_unstable(feature = "maybe_uninit_uninit_array")]
    pub const fn uninit_array<const N: usize>() -> [Self; N];

    #[unstable(feature = "maybe_uninit_array_assume_init")]
    #[rustc_const_unstable(feature = "maybe_uninit_array_assume_init")]
    pub const fn array_assume_init<const N: usize>(array: [Self; N]) -> [T; N];
}

Sub-tracking issues:

Steps / History

Unresolved Questions

  • Should MaybeUninit::uninit_array::<LEN>() be stabilised if it can be replaced by [const { MaybeUninit::uninit() }; LEN] ?
  • What other APIs should be added for arrays?
  • Is array_assume_init the right pattern, or should we convert from [MaybeUninit<T>, N] back to MaybeUninit<[T; N]> first?
@clarfonthey clarfonthey added C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Apr 16, 2022
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Apr 16, 2022
…up, r=dtolnay

MaybeUninit array cleanup

* Links `MaybeUninit::uninit_array` to meta-tracking issue
* Links `MaybeUninit::array_assume_init` to meta-tracking issue
* Unstably constifies `MaybeUninit::array_assume_init`

Another thing worth mentioning: this splits the const feature flag for `maybe_uninit_uninit_array` into `const_maybe_uninit_uninit_array` to avoid weird cases where only one gets stabilised.

Note that it may be desired to keep the `array_assume_init` method linked to its dedicated issue, but at least for now, I decided to link to the meta-tracking issue so that all of the methods lead users to the same place. But I can revert that bit if desired.

The meta-tracking issue that I filed is rust-lang#96097.
@joboet
Copy link
Contributor

joboet commented Apr 16, 2022

I'm a bit concerned about the size and inconsistency of this API, as the corresponding functions for single values are all methods, while for arrays and slices we have associated functions. For example, array_assume_init fundamentally does the same thing as assume_init, but is called in a different way, with a different name. If the API was complete (zeroed_array is currently missing, among others), we would have 8 or so functions for arrays alone (currently only two, but a lot of functionality still unnecessarily requires unsafe).

I would rather prefer a trait-based API like this:

unsafe trait Uninitialized {
    type Initialized: ?Sized;

    fn uninit() -> Self
    where Self: Sized;
    fn zeroed() -> Self
    where Self: Sized;
    
    unsafe fn assume_init(self) -> Self::Initialized
    where Self: Sized;
    unsafe fn assume_init_ref(&self) -> &Self::Initialized;

    ...
}

unsafe impl<T> Uninitialized for MaybeUninit {...}
unsafe impl<T, const N: usize> Uninitialized for [T; N] {...}
unsafe impl<T> Uninitialized for [T] {...}

or with a marker trait like I proposed here.

@Nilstrieb
Copy link
Member

I don't think it's worth making a trait for this. I've had many use cases for uninit_array before, and none for a generic solution. MaybeUninit is often used with arrays, since that's where initialization tends to be the most expensive.

While it is a trivial function to write yourself, I do think that it's worth it to stabilize maybe_uninit_uninit_array. It's a very common use case, and the alternative is to write am unsettling MaybeUninit::uninit ().assume_init() which is not very nice and makes it harder to audit the code.

@clarfonthey
Copy link
Contributor Author

clarfonthey commented Sep 10, 2022

Another thing worth asking: why not have methods directly implemented on arrays? You'd still need some form of uninit_array, but instead of MaybeUninit::array_assume_init(arr), you'd just do arr.assume_init(), and the implementation is on [MaybeUninit<T>; N].

This seems possible specifically because the types exist in core, but open to other interpretations.

I also suggested perhaps just adding Index and IndexMut implementations for MaybeUninit<[T]> that return MaybeUninit<T> and MaybeUninit<[T]> references, then removing the transposed versions of [MaybeUninit<T>] methods.

Of course, the weirdness of MaybeUninit<[T]> is that the length of the slice isn't uninit, just the data in the slice itself, since the pointer metadata is totally separate. But I feel like this is only a weird quirk that quickly becomes natural after a short explanation. Plus, this applies already to other DSTs like dyn Trait, even though a lot of methods require Sized. In principle you could coerce &mut MaybeUninit<T> into &mut MaybeUninit<dyn Trait> if it were more idiomatic and that would make sense as a type, even though you couldn't do much with it.

@WaffleLapkin
Copy link
Member

@clarfonthey with the current implementation, MaybeUninit<[T]> is not possible at all, MaybeUninit requires T: Sized.

@SUPERCILEX
Copy link
Contributor

SUPERCILEX commented Oct 15, 2022

Docs patch extracted from #102023:

Subject: [PATCH] Add MaybeUninit array transpose impls

Signed-off-by: Alex Saveau <[email protected]>
---
Index: library/core/src/mem/maybe_uninit.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs
--- a/library/core/src/mem/maybe_uninit.rs	(revision 8147e6e427a1b3c4aedcd9fd85bd457888f80972)
+++ b/library/core/src/mem/maybe_uninit.rs	(date 1665873934720)
@@ -117,15 +117,12 @@
 /// `MaybeUninit<T>` can be used to initialize a large array element-by-element:
 ///
 /// ```
-/// use std::mem::{self, MaybeUninit};
+/// use std::mem::MaybeUninit;
 ///
 /// let data = {
-///     // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
-///     // safe because the type we are claiming to have initialized here is a
-///     // bunch of `MaybeUninit`s, which do not require initialization.
-///     let mut data: [MaybeUninit<Vec<u32>>; 1000] = unsafe {
-///         MaybeUninit::uninit().assume_init()
-///     };
+///     // Create an uninitialized array of `MaybeUninit`.
+///     let mut data: [MaybeUninit<Vec<u32>>; 1000] = MaybeUninit::uninit().transpose();
 ///
 ///     // Dropping a `MaybeUninit` does nothing, so if there is a panic during this loop,
 ///     // we have a memory leak, but there is no memory safety issue.
@@ -133,25 +130,23 @@
 ///         elem.write(vec![42]);
 ///     }
 ///
-///     // Everything is initialized. Transmute the array to the
-///     // initialized type.
-///     unsafe { mem::transmute::<_, [Vec<u32>; 1000]>(data) }
+///     // Everything is initialized. Convert the array to the initialized type.
+///     unsafe { MaybeUninit::<[Vec<_>; 1000]>::assume_init(data.transpose()) }
 /// };
 ///
-/// assert_eq!(&data[0], &[42]);
+/// assert_eq!(&data[42], &[42]);
 /// ```
 ///
 /// You can also work with partially initialized arrays, which could
 /// be found in low-level datastructures.
 ///
 /// ```
 /// use std::mem::MaybeUninit;
 /// use std::ptr;
 ///
-/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
-/// // safe because the type we are claiming to have initialized here is a
-/// // bunch of `MaybeUninit`s, which do not require initialization.
-/// let mut data: [MaybeUninit<String>; 1000] = unsafe { MaybeUninit::uninit().assume_init() };
+/// // Create an uninitialized array of `MaybeUninit`.
+/// let mut data: [MaybeUninit<String>; 1000] = MaybeUninit::uninit().transpose();
 /// // Count the number of elements we have assigned.
 /// let mut data_len: usize = 0;
 ///

JohnTitor added a commit to JohnTitor/rust that referenced this issue Oct 16, 2022
…r=scottmcm

Add MaybeUninit array transpose From impls

See discussion in rust-lang#101179 and rust-lang#96097. I believe this solution offers the simplest implementation with minimal future API regret.

`@RalfJung` mind doing a correctness review?
bors added a commit to rust-lang-ci/rust that referenced this issue Oct 16, 2022
Deprecate uninit_array

Now that rust-lang#98304 has gone through, `uninit_array` should be removed since creating a const array is the One True Way of doing things.

Tracking issue: rust-lang#96097
thomcc pushed a commit to tcdi/postgrestd that referenced this issue Feb 10, 2023
Add MaybeUninit array transpose From impls

See discussion in rust-lang/rust#101179 and rust-lang/rust#96097. I believe this solution offers the simplest implementation with minimal future API regret.

`@RalfJung` mind doing a correctness review?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants