Consider the example in the implementation of std::sync::Weak
#[repr(C)]
struct ArcInner<T: ?Sized> {
strong: atomic::AtomicUsize,
weak: atomic::AtomicUsize,
data: T,
}
#[stable(feature = "arc_weak", since = "1.4.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "ArcWeak")]
pub struct Weak<
T: ?Sized,
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
> {
ptr: NonNull<ArcInner<T>>,
alloc: A,
}
impl<T: ?Sized, A: Allocator> Weak<T, A> {
#[inline]
fn inner(&self) -> Option<WeakInner<'_>> {
let ptr = self.ptr.as_ptr();
if is_dangling(ptr) {
None
} else {
// We are careful to *not* create a reference covering the "data" field, as
// the field may be mutated concurrently (for example, if the last `Arc`
// is dropped, the data field will be dropped in-place).
Some(unsafe { WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } })
}
}
}
The comment says that the intent is to avoid creating a reference covering the "data" field. However, ptr
has type NonNull<ArcInner<T>>
, doesn't the dereference *ptr
produce a reference to ArcInner<T>
which covers the "data" field? If *ptr
does not produce that reference, why is it necessary to create WeakInner<'_>
? (*(self.ptr)).strong
or (*(self.ptr)).weak
is sufficient to manipulate the strong
and weak
fields, which does not touch the potentially corrupt data?