Skip to content

Tracking Issue for maybe_uninit_fill #117428

Open

Description

@jmillikin

View all comments

Feature gate: #![feature(maybe_uninit_fill)]

This is a tracking issue for ACP rust-lang/libs-team#156

Public API

// in core

impl<T> [MaybeUninit<T>] {
    pub fn write_filled(&mut self, value: T) -> &mut [T]
    where
        T: Clone;

    pub fn write_with<F>(&mut self, mut f: F) -> &mut [T]
    where
        F: FnMut(usize) -> T;

    pub fn write_iter<I>(&mut self, it: I) -> (&mut [T], &mut [MaybeUninit<T>])
    where
        I: IntoIterator<Item = T>;
}

Steps / History

Unresolved Questions

  • Naming: these were originally named fill, fill_with, and fill_from to be consistent consistent with slice::{fill_with,fill_from}. Since changing to slice-inherent methods, these needed to be renamed to avoid conflict. Should we change to naming that keeps fill? Amanieu suggested fill_init at Add inherent versions of MaybeUninit methods for slices #129259 (comment).

Footnotes

  1. https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html ↩

Activity

  1. added
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    T-libs-api[DEPRECATED; DO NOT USE]
    on Oct 31, 2023
  2. jmillikin commented on Nov 1, 2023

    @jmillikin
    ContributorAuthor

    Why do the functions return a shared reference instead of a mutable reference?

    #80376

    If a &mut [T] is returned for T: Clone, there's no indication to downstream code that writing values to that slice will cause them to leak.

  3. jmillikin commented on Nov 15, 2023

    @jmillikin
    ContributorAuthor

    I'm not planning to implement a <T: Clone>(&mut [MaybeUninit<T>], T) -> &mut [T] function as part of this feature, though I think it would be fine for someone else to do so if they have a view on what the correct Drop semantics should be.

    Alternatively, maybe a -> &mut [ManuallyDrop<T>] would work for your use case? That would be safe to add since it makes the special-ness clear.

  4. jmillikin commented on Nov 24, 2023

    @jmillikin
    ContributorAuthor

    @python-ast-person If I understand you correctly, then you'd use the proposed fill_cloned to implement in-place construction of a Box<[T]> like this (playground link):

    // presumed to exist as part of unsized MaybeUninit
    impl<T> MaybeUninit<T> {
        fn transpose_slice<'a>(this: &'a mut MaybeUninit<[T]>) -> &'a mut [MaybeUninit<T>] {
            unimplemented!()
        }
    }
    
    fn make_box<T: ?Sized>(
        size: <T as Pointee>::Metadata,
        filler: impl FnOnce(&mut MaybeUninit<T>) -> &mut T,
    ) -> Box<T> {
        unimplemented!()
    }
    
    fn main() {
        let boxed = make_box::<[String]>(10, |u: &mut MaybeUninit<[String]>| {
            let a = String::from("a");
            let transposed = MaybeUninit::transpose_slice(u);
            MaybeUninit::fill_cloned(transposed, &a);
            unsafe { MaybeUninit::slice_assume_init_mut(transposed) }
        });
        println!("boxed: {boxed:?}");
    }

    Having MaybeUninit::fill_cloned() return a &mut [T] would provide only slightly cleaner code (one less line).

    If you want construction of Box<[T]> to be possible without any unsafe at all, then you might want a helper function that takes ownership of its input, which should be Drop-safe. This can't be part of MaybeUninit directly because Box isn't available in the core crate, but it should be straightforward to implement if you already have unsized MaybeUninit in your codebase.

    // internally does fill_cloned + slice_assume_init_mut() as above
    fn fill_box_cloned<T: Clone>(this: Box<MaybeUninit<[T]>>,  value: &T) -> Box<[T]>;
  5. ajwock commented on Feb 17, 2024

    @ajwock
    Contributor

    Hey, a couple of opinions on the API here.

    One, I suggest that fill_from returns -> (&'a mut [T], &'a mut [MaybeUninit]) where the second slice is the uninitialized remainder. This produces a more useful API while harmless to anyone who doesn't want that remainder- they can just throw it away.

    Another thing: I disagree that having fill_cloned should return an immutable reference. The &mut [T] returned from fill_cloned will already cause the destructors to be leaked regardless of mutability, or regardless of whether new values are written into the mutable slice. I don't know of any other method in std which takes a mutable reference and returns an immutable reference to the safe object and, in my opinion, should only be used where necessary to prevent true safety issues.

    A box version was in my original proposal. I removed it due to a suggestion referring to rust-lang/rfcs#2884. Perhaps these two things should coexist, however.

  6. ajwock commented on Feb 18, 2024

    @ajwock
    Contributor

    jmillikin has allowed me to take over implementation: #121280

  7. ajwock commented on Feb 20, 2024

    @ajwock
    Contributor

    rust-lang/libs-team#338

    This idea is my response to the (legitimate) criticism of these APIs that they could lead to destructor leaks.

  8. cynecx commented on Feb 23, 2024

    @cynecx
    Contributor

    It would've been nice to also have the "same" interface for boxed slices:

    impl<T, A: Allocator> Box<[MaybeUninit<T>], A> {
        pub fn fill(boxed: Box<[MaybeUninit<T>], A>, value: T) -> Box<[T], A>
        where
            T: Clone;
        
        // EDIT: Not needed because it is covered by above.
        // pub fn fill_cloned(boxed: Box<[MaybeUninit<T>], A>, value: &T) -> Box<[T], A>
        // where
        //     T: Clone;
    
        pub fn fill_with<F>(boxed: Box<[MaybeUninit<T>], A>, f: F) -> Box<[T], A>
        where
            F: FnMut() -> T;
    
        pub fn fill_from<I>(boxed: Box<[MaybeUninit<T>], A>, iter: I) -> Box<[T], A>
        where
            I: Iterator<Item = T>;
    }

    Does this need a new ACP or would it be possible to also consider this under maybe_uninit_fill?

  9. cynecx commented on Feb 23, 2024

    @cynecx
    Contributor

    It might be useful for fill_with's F to accept an index:

    pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit<T>], f: F) -> &'a [T]
    where
        F: FnMut(usize) -> T; // <-- accepts an index (current element that is being written to)

    Alternatively, you could just store and mutate a counter inside the passed in closure, but this might be more convenient...

  10. ajwock commented on Feb 27, 2024

    @ajwock
    Contributor

    Having the same interface for Box needs a new ACP. I agree that it would be useful in some cirucumstances. Also, if you are going to make such an ACP, note that fill_cloned is not necessary due to specialization. See the implementation of the slice’s fill.

    re: the function accepting an index. This is a different API from the slice’s fill_with method. Ideally MaybeUninit slice APIs mirror the slice APIs, especially if occupying the same name. Loading your closure with index state is trivial, and any use case that doesn’t want the index will have to throw it away.

  11. cynecx commented on Feb 27, 2024

    @cynecx
    Contributor

    Also, if you are going to make such an ACP, note that fill_cloned is not necessary due to specialization. See the implementation of the slice’s fill.

    Hmm, those are quite "different" APIs. Slice's fill and co doesn't allow a fully safe way to initialize a boxed slice.

    impl<T, A: Allocator> Box<[MaybeUninit<T>], A> {
        pub fn fill_cloned(boxed: Box<[MaybeUninit<T>], A>, value: &T) -> Box<[T], A>
        where
            T: Clone;
    }
    
    // vs
    
    impl<T> [T] {
        pub fn fill(&mut self, value: T)
        where
            T: Clone,
    }

    Not sure if that's the one you mentioned, but I've looked through several places and I can't seem to find a similar api:

    EDIT (To reduce additional noise here):

    I think the confusion here is simply the outdated OP issue description. I was under the impression that the API design written above was the most recent one but as ajwock mentioned below, the preference is to use specialization to avoid having both fill and fill_cloned which makes total sense. It would be great if someone could update the issue description though, just to help future readers :)

  12. 1 remaining item

  13. Rua commented on Jun 6, 2024

    @Rua
    Contributor

    I think fill_with should take a closure with one parameter, containing the index to be filled. That would allow the user to do different things depending on that. Compare array::from_fn.

  14. RalfJung commented on Feb 21, 2025

    @RalfJung
    Member

    I think fill_with should take a closure with one parameter, containing the index to be filled. That would allow the user to do different things depending on that. Compare array::from_fn.

    Yeah, came here to say the same. Unfortunately slice::fill_with is already stable and does not take an index.

  15. RalfJung commented on Feb 21, 2025

    @RalfJung
    Member

    @tgross35 I am confused by your edits to the tracking issue. Is there a PR that renames fill to write_filled etc? There is none linked above that I could find, and the current nightly still has fill.

  16. tgross35 commented on Feb 21, 2025

    @tgross35
    Member

    That is in #135394, I updated this with the assumption that the change would go through reasonably quick. I'll change it back for now and nominate the PR.

  17. added a commit that references this issue on Mar 19, 2025
  18. added a commit that references this issue on Mar 20, 2025
  19. added a commit that references this issue on Apr 2, 2025
  20. ljtpetersen commented on Jun 30, 2025

    @ljtpetersen

    For the write_with and write_iter methods, I think it is important to document that the values in the original MaybeUninit array need to be manually dropped later. Alternatively, we could implement a transparent wrapper which will drop it when it goes out of scope and which implements DerefMut for the slice. I believe one of these should be done, and the choice is whether we want to require the user to manually drop it.

    I believe the better option is to create a wrapping structure. This is because it will allow users to avoid unsafe code. In its current state, using write_iter does not always avoid unsafe, because the user might need to drop it, in which case they currently need to use assume_init_drop. Also, writing a wrapping structure means we can implement methods that will take ownership of the elements, which write_iter currently can't do. For example, we could implement IntoIterator on the wrapping structure.

    Something like

    #[repr(transparent)]
    #[derive(Debug)]
    pub struct Wrapper<T>(&[MaybeUninit<T>]);
    
    impl<T> Drop for Wrapper<T> {
        fn drop(&mut self) { todo!() }
    }
    
    impl<T> Deref for Wrapper<T> {
        type Target = [T];
        
        const fn deref(&self) -> &[T] { todo!() }
    }
    
    impl<T> DerefMut for Wrapper<T> {
        const fn deref_mut(&mut self) -> &mut [T] { todo!() }
    }
    
    impl<T> IntoIterator for Wrapper<T> {
        type Item = T;
        type IntoIter = /* todo */;
        fn into_iter(self) -> <Wrapper::<T> as IntoIterator>::IntoIter { todo!() }
    }

    I am willing to implement this if we want to go with it, since I find it interesting. Though, I don't know how contribution works for the std lib.

  21. ajwock commented on Jun 30, 2025

    @ajwock
    Contributor

    rust-lang/libs-team#338

    I've had the same thought, though admittedly I personally did not have the use case myself and I implemented that in response to the same concerns you had. I'll go ahead and publish that as a crate as the libs-team suggested. Perhaps it will find some use if it is made available.

  22. Morgane55440 commented on Apr 1, 2026

    @Morgane55440

    fill_with was mentionned to me for a problem i was tryng to solve, but it turned out to not be good enough, so i'd like it to be mentionned at least even if my fix also has its flaws.

    i believe that fill_with should take a FnMut(&mut [T]) -> T, as allowing lookback during the creation of the elements.
    i can understand how it could be too clunky, and is not how things like array::from_fn work

  23. added a commit that references this issue on Aug 11, 2026
  24. added a commit that references this issue on Aug 11, 2026
  25. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
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

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions