Closed
Description
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
}
Output:
dropping A
dropping A
dropping A
dropping B
dropping B
dropping B
dropping B
dropping B
dropping B