Replacing Box::new_uninit(), assume_init()

I am working on an ffi crate and realized I can target a fairly old rust toolchain with little effort.
However, I need to replace this pattern:

    let mut mem: Box<MaybeUninit<sys::c_struct>> = Box::new_uninit();
    // initialize in C
    let mem: Box<sys::c_struct> = unsafe { mem.assume_init() };
    let inner: *mut sys::c_struct= Box::into_raw(mem);

Then of course, dropped with Box::from_raw

I think it could be replaced with just the following:

    let mut mem: Box<MaybeUninit<sys::c_struct >> = Box::new(MaybeUninit::uninit());
    let inner: *mut sys::c_struct = Box::into_raw(mem) as *mut sys::c_struct;

Is this alright to do, or are there better ways?
Thanks

Assuming the struct is small enough that it isn’t an issue for it to be on the stack temporarily, it should be equivalent to the original.

You could also std::alloc::alloc() the memory directly, without getting it from Box. It’s fine to convert such a pointer into Box<T> as long as the Layout used for allocation matches the layout of T (docs).

6 Likes

Try using the pin-init crate.

1 Like

If all you want is the pointer, and you initialization cannot fail, then yeah, just doing

let inner: *mut sys::c_struct = alloc::alloc(Layout::new::<sys::c_struct>()).cast();

would be fine too, if the struct is big enough that you run into trouble with Box::new(MaybeUninit::uninit()).

Don't forget to check for a null pointer indicating allocation failure! Box::new() will abort or panic but alloc() will just return null.

(I should perhaps have mentioned this in my previous post, too, but seeing the full line written out reminded me that they are not perfect substitutes in this way.)

2 Likes

Very good point!

Also another reason to prefer NonNull<sys::c_struct> to *mut sys::c_struct since it'll push you into remembering that.

(Man I wish that nullable pointers were also spelled Option<*mut T>. Definitely a thing for the "if I had a time machine" list.)