Yes. Notice however that the call to Layout::for_value would still be UB and also needs to be fixed.
Also, there is no need for literally using pointer arithmetic.
I think the following should work (and I should really test that…):
unsafe fn drop_slow(&mut self) {
let old_weak = self.inner().weak.fetch_sub(1, Release);
let ptr = self.ptr.as_ptr();
let layout = Layout::for_value(&*ptr);
// Destroy the data at this time, even though we may not free the box
// allocation itself (there may still be weak pointers lying around).
ptr::drop_in_place(&mut self.ptr.as_mut().data);
if old_weak == 1 {
atomic::fence(Acquire);
Heap.dealloc(ptr as *mut u8, layout)
}
}
Note that this computes the layout even when that is not needed. But we are talking about a one-time cost per Arc, this is probably not significant (and would be fixed at the expense of some code duplication).
I think reordering the fetch_sub up to the beginning of the function is okay, but if it is not, this should also work:
unsafe fn drop_slow(&mut self) {
let ptr = self.ptr.as_ptr();
let layout = Layout::for_value(&*ptr);
// Destroy the data at this time, even though we may not free the box
// allocation itself (there may still be weak pointers lying around).
ptr::drop_in_place(&mut (*ptr).data);
// We must not use the type &(mut) ArcInner from now on, because that
// type is actually no longer valid.
if (*ptr).weak.fetch_sub(1, Release) == 1 {
atomic::fence(Acquire);
Heap.dealloc(ptr as *mut u8, layout)
}
}
I actually think not obtaining a pointer to the inner part three times makes the code overall more readable.