Can Box/Arc failed to allocate and report error like C++ std::bad_alloc?

In C++, new operation can fail and either return a null pointer or throw a std::bad_alloc exception, which depends on what new function you called. I wondered if there's similar feature in Rust, for example a version of Box/Arc returns a Result?

P.S. I can do this in unsafe code with alloc, but I do want to use Box/Arc in this case.

It's unstable.

1 Like

As mentioned, there are the unstable functions Box::try_new() and Arc::try_new(). But right now, the only stable functions for safe fallible allocation are Vec::try_reserve() and Vec::try_reserve_exact(). Using the Vec function, the best you can do is get a Box<[T; 1]>.

use std::collections::TryReserveError;

pub fn try_new_box<T>(x: T) -> Result<Box<[T; 1]>, TryReserveError> {
    let mut vec = Vec::new();
    vec.try_reserve_exact(1)?;
    vec.push(x);
    let slice = vec.into_boxed_slice();
    let array = slice.try_into().unwrap_or_else(|_| unreachable!());
    Ok(array)
}
1 Like

I suddenly have a question, may out of topic. We normally create a Box like this:

let box = Box::new(Foo::new());

In this way, won't the program do an extra copy from the stack to the heap? Is there anything we can do other than expecting the compiler to do the optimization? As far as I know, new_in_place or placement new is still in discussion and have a long time until we can use it.

Best you can do for now is call (still unstable) Box::new_uninit.

And have you type has a fn new_uninit(this: &mut MaybeUninit<Self>, params...).

Then call assume_init.

(You can check the source for stable implementation. But honestly it's basically calling alloc with fancy wrappers)

1 Like

If you have a Foo::new_uninit() function available, you can similarly use a Vec trick to avoid a manual alloc():

pub fn new_foo_in_place() -> Box<Foo> {
    let mut vec = Vec::with_capacity(1);
    // SAFETY: We call `set_len` after initializing the element.
    unsafe {
        Foo::new_uninit(&mut vec.spare_capacity_mut()[0]);
        vec.set_len(1);
    }
    let ptr = Box::into_raw(vec.into_boxed_slice());
    // SAFETY: We go from a length-1 `Box<[T]>` to a `Box<T>`.
    unsafe { Box::from_raw(ptr.cast()) }
}

// or, a bit more precariously:

use std::mem::ManuallyDrop;

pub fn new_foo_in_place() -> Box<Foo> {
    let ptr: *mut Foo = ManuallyDrop::new(Vec::with_capacity(1)).as_mut_ptr();
    unsafe {
        Foo::new_uninit(&mut *ptr.cast::<MaybeUninit<Foo>>());
        Box::from_raw(ptr)
    }
}
4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.