I'm working on a project in a no_std
context that needs to be very careful with allocations and handle errors from the allocator when it's out of memory. We have a number of places that use Arc<T>
and there doesn't currently appear to be a way of creating a new Arc<T>
and handling an allocation failure that doesn't rely on unwinding and using catch_unwind. I would like to add one.
We have the ability to create a Box<T>
like this:
pub fn try_box<T: Sized>(x: T) -> Result<Box<T>, OOM> {
unsafe {
let layout = Layout::new::<T>();
let ptr = crate::alloc::alloc::alloc(layout);
if ptr.is_null() {
Err(OOM)
} else {
core::ptr::write::<T>(ptr as *mut T, x);
Ok(Box::from_raw(ptr as *mut T))
}
}
}
But this approach does not work for Arc<T>
because the creation of an Arc<T>
allocates an ArcInner<T>
and ArcInner<T>
is a private type.
There seems to be two ways of providing this functionality and I would be willing to do the legwork for implementing either but wanted to receive feedback before heading down a wrong path.
- Expose an interface for creating an
ArcInner<T>
and to create anArc<T>
from aBox<ArcInner<T>>
It could look something like this:
#[repr(C)]
pub struct ArcInner<T: ?Sized> {
strong: atomic::AtomicUsize,
weak: atomic::AtomicUsize,
data: T,
}
impl<T: ?Sized> ArcInner<T> {
pub fn new(data: T) -> Self {
ArcInner {
strong: atomic::AtomicUsize::new(1),
weak: atomic::AtomicUsize::new(1),
data,
}
}
}
impl<T: ?Sized> Arc<T> {
// find a better name
pub fn from_inner(inner: Box<ArcInner>) -> Self {
Self::from_ptr(Box::into_raw(inner))
}
}
- provide a
try_new
function that returns an Err on allocation failure.
imp<T: ?Sized> Arc<T> {
pub fn try_new(data: T) -> Result<Self, TryNewError> {
...
}
}
Do either of thees approaches seem acceptable? Are there other alternatives I've overlooked? Any feedback appreciated.