I am trying to unwrap an Arc<dyn T> returned by a library, since I have no reason to keep the reference counting behavior, and I do know there's an Arc::try_unwrap but it seems it can't be used to unwrap DSTs without the unsized_locals feature.
It's not a huge deal, I know, since there's zero overhead in Arc::deref, I don't actually need mutable access, and unwrapping to a Box would incur the same overhead as the Arc::drop you're avoiding anyway..
There's no way to do this currently is there? Otherwise I'd consider discussing adding a method / TryFrom implementation for this in IRLO (except that above I've kinda convinced myself that I don't really need it anyway )
I would be very surprised if it was possible to do this without allocating a whole new Box, because you raise the question, what happens to the refernce counts? I don't think it's feasible for Box<T> to "hide" a few extra words in the heap allocation even though they're not included in the T.
Oh, what I was implying was semantics similar to Arc::try_unwrap, where it first checks that there are no other strong or weak references before taking ownership. This proposed method would be exactly the same, but usable with dyn T instead of just T: Sized.
Edit: also, yes, I am saying it'd allocate a new Box and move the dyn T into it.
Right. The issue is I don't control the trait, or I'd just have the constructor return Box<dyn T> instead, as there is a impl<T: ?Sized> From<Box<T>> for Arc<T>..
Ha, yeah I was eyeing Arc::get_mut but I believe your solution is UB here:
let rv = Arc::get_mut(&mut arc).map(|p| {
unsafe { Box::from_raw(p as *mut (dyn SomeTrait +'_)) }
});
I believe this is UB because Box::from_raw expects a pointer which originated from Box::into_raw. On top of that, you can also see the current implementation of Arc stores the T after the strong and weak counts in a #[repr(C)] struct, which means Box::drop would end up calling free on the middle of an allocation. This is all subject to change as implementation detail as well..
#![feature(set_ptr_value)]
extern crate alloc;
use core::mem;
use core::ptr;
use alloc::alloc::{alloc, Layout};
use alloc::sync::Arc;
fn to_box_leaky<T: ?Sized>(mut arc: Arc<T>) -> Option<Box<T>> {
unsafe {
let src: *mut T = Arc::get_mut(&mut arc)?;
let dst = alloc(Layout::for_value(&*src));
let len = mem::size_of_val(&*src);
ptr::copy_nonoverlapping(src as *const T as *const u8, dst, len);
mem::forget(arc);
Some(Box::from_raw(src.set_ptr_value(dst)))
}
}
Then indeed you would need unsafe or some lib that does it for you. But, as you noticed, it is currently not possible for non-stdlib-code to free the backing memory of an Arc without dropping the pointee when the pointee is unsized (FWIW, here is my approach if it wasn't for that caveat: it handles ZSTs and follows SB rules). This dooms your approach to be leaky, sadly.
What I'd suggest here instead, based on the OP, is to feature your own quasi-Box with the same semantics as Box, but for the drop glue. I was about to write one, but then I remembered @CAD97's ::rc-box crate, with a very interesting impl: