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.
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