Skip to content

Infinite loop when trying to fill a Vector of MaybeUninit<SomeZeroSizedType> #80747

Closed
@PaulGrandperrin

Description

@PaulGrandperrin

I tried to reduce the demonstration to the minimum possible.

I wrote a function that fills a vector of MaybeUninit<T> values that I think is safe (free of UB).
The code works well with cargo miri run.

But when calling this function with a zero-sized type, the execution goes into an infinite loop.
The exact same code without the MaybeUninit logic works as expected.

Here is the code:

#![feature(maybe_uninit_extra)] // not important, works also without
use core::mem::{self, MaybeUninit};

struct A(u8);
impl Default for A {
    fn default() -> Self {
        Self(0)
    }
}
impl Drop for A {
    fn drop(&mut self) {
        println!("dropping A");
    }
}

struct B;
impl Default for B {
    fn default() -> Self {
        Self
    }
}
impl Drop for B {
    fn drop(&mut self) {
        println!("dropping B");
    }
}

// Fills a vector of uninitialized T with their default values
fn fill_default_uninit<T: Default>(mut v: Vec<MaybeUninit<T>>) -> Vec<T> {
    v.iter_mut().for_each(|i|{
        i.write(T::default()); // without the nightly feature: unsafe{i.as_mut_ptr().write(T::default())};
    });
    unsafe { mem::transmute::<_, _>(v) } // this is safe because the Vec is now fully initialized
}

// Fills a vector of T with their default values, overriding the previous ones
fn fill_default_init<T: Default>(mut v: Vec<T>) -> Vec<T> {
    v.iter_mut().for_each(|i|{
        *i = T::default();
    });
    v
}

fn main() {
    // create an uninitialized vector of 3 As
    let mut v: Vec<MaybeUninit<A>> = Vec::with_capacity(3);
    unsafe{v.set_len(v.capacity())}; // this is safe because the elements are MaybeUninit
    let v = fill_default_uninit(v); // no problem
    drop(v); // all 3 As are dropped
    
    
    // create an uninitialized vector of 3 Bs
    let mut v: Vec<MaybeUninit<B>> = Vec::with_capacity(3);
    unsafe{v.set_len(v.capacity())}; // this is safe because the elements are MaybeUninit
    //let v = init_default(v); //  <------------------------------------------------------ INFINITE LOOP HERE
    //drop(v);
    
    // now let's try the same code for B without MaybeUninit
    let mut v = Vec::with_capacity(3);
    for _ in 0..3 { v.push(B::default()) }

    let v = fill_default_init(v); // no problem, here the 3 old Bs will be dropped
    drop(v); // no problem, the 3 new Bs will be dropped
}

(Playground)

Output:

dropping A
dropping A
dropping A
dropping B
dropping B
dropping B
dropping B
dropping B
dropping B

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-collectionsArea: `std::collections`A-docsArea: Documentation for any part of the project, including the compiler, standard library, and toolsT-libs-apiRelevant to the library API 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