How to avoid copying data with MaybeUninit?

I expected MaybeUninit to be a copyless type but according to this snippet (even in release build) it does copy internal value: https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gist=99e1edd47c6886a8f35807aa7d246323

What's happening here? Am I missing something?

Old deprecated API std::mem::uninitialized() does not copy data.

As far as I understand, the better way to do this - avoid to use both API, and use array with zero values ( https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=813f8f313e5f6a247d4848a2a7aed183 ) , because copying is potentially less effecient (allocating new space may eat many CPU) than filling with zeros.

This kind of copy may appear when a let x = y move happens: the compiler does not yet manage to always elide the copy.

Had you done

let mut p = mem::uninitialised::<[u8; ARRAY_LEN]>();
let x = p;

you would most probably have gotten such memcpy too.

Obviously the dangerous and deprecated mem::uninitialised does not lead to writing that extra move as MaybeUninit::assume_init() does.

That's what MaybeUninit::get_ref() and MaybeUninit::get_mut() are for: they also assume init, only this time they return a reference to the (assumed initialised) data:

#![feature(maybe_uninit_ref)]

use std::mem::MaybeUninit;

const ARRAY_LEN: usize = 1024 * 1204;
type Array = [u8; ARRAY_LEN];

fn main ()
{
    let mut raw_array = MaybeUninit::<Array>::uninit();
    let at_array: &mut Array = unsafe { 
        //for example - pass pointer to FFI for init data
        ::libc::memset(
            raw_array.as_mut_ptr() as *mut _,
            42,
            ARRAY_LEN,
        );
        raw_array.get_mut()
        /* or, without the unstable feature:
        /// Use a function to prevent having an unbounded lifetime
        unsafe
        fn get_mut<T> (raw_array: &'_ mut MaybeUninit<T>) -> &'_ mut T
        {
            
            &mut *raw_array.as_mut_ptr()
        }
        get_mut(&mut raw_array)
        */
    };
    println!("{:p} -> {:?}", at_array, &at_array[.. 10]);
}

Note that .get_ref() and .get_mut(), on the other hand, do not run the destructor, which means that memory can be leaked when then innards have Drop glue (which should not be the case when doing FFI):

let mut raw_vector = mem::MaybeUninit::<Vec<u8>>::uninit();
let at_vector = unsafe {
    raw_vector.as_mut_ptr().write(vec![42]);
    raw_vector.get_mut()
};
assert_eq!(&at_vector[..], &[42][..]);
mem::drop(raw_vector); // Memory leak
2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.