Follow-up of Why doesn't Weak::new() work when Rc::downgrade() does?
When attempting to implement Weak::new()
in a way that would NOT require it to allocate memory for the underlying type even though it is never going to be used, I hit a roadblock.
The definition of RcBox<T>
is rather simple:
struct RcBox<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
And the goal here is to create a RcBox<T>
which will actually NOT contain any value
. Essentially, a RcBox<()>
.
However, there is a snag. *mut RcBox<()>
is a thin pointer but *mut RcBox<T>
is potentially a fat pointer. We have the data part of this fat pointer right, but there are many different cases of fat pointers so attempting to synthetize the rest is hard.
As can be seen in the linked question, I can make it work for just trait objects:
impl<T: ?Sized> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
let boxed = Box::into_raw(box RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: (),
});
let ptr = if size_of::<*mut ()>() == size_of::<*mut T>() {
let ptr: *mut RcBox<T> = transmute_copy(&boxed);
ptr
} else {
let ptr: *mut RcBox<T> = transmute_copy(&TraitObject {
data: boxed as *mut (),
vtable: null_mut(),
});
ptr
};
Weak { ptr: Shared::new(ptr) }
}
}
}
However this won't work with str
(for example).
I made another attempt trying to isolate the fixed-size portion of RcBox
while letting the compiler infer the fat part of the pointer:
struct RcBox<T: ?Sized> {
counters: RcBoxCounters<T>,
value: T,
}
struct RcBoxCounters<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
_phantom: PhantomData<T>,
}
impl<T: ?Sized> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
let boxed = Box::into_raw(box RcBox::Counters::new(0, 1));
Weak { ptr: Shared::new(boxed as *mut RcBox<T>) }
}
}
}
which sounds very clever until the compiler squashes your enthusiasm:
error[E0375]: implementing the trait `CoerceUnsized` requires multiple coercions --> <anon>:58:40 | 58 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<RcBox<U>> for RcBox<T> {} | ^^^^^^^^^^^^^^^^^^^^^^^ requires multiple coercions | = note: `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced = note: currently, 2 fields need coercions: counters (RcBoxCounters<T> to RcBoxCounters<U>), value (T to U)
That is:
PhantomData
in RcBoxCounters
,So, is there a way to fix Weak::new()
so that it stops allocating extraneous (unnecessary) memory?
Note: I do mean allocating only space for the two counters, allocating large and trimming afterward does NOT help.
Note: It has been remarked that one could use an Option
or special value to denote the absence of value. This requires branching on each method, which may not be desirable. I prefer learning to fiddle with fat pointers.
The new operator should only be used if the data object should remain in memory until delete is called. Otherwise if the new operator is not used, the object is automatically destroyed when it goes out of scope.
In C language, we use the malloc() or calloc() functions to allocate the memory dynamically at run time, and C++ also supports these functions. But, in C++, allocation and deallocation are done manually.
but... you cannot dereference a weak pointer. you have to lock it, get a shared one from it and use that one. You are supposed to convert/create a shared_ptr from them before accessing the raw pointer to avoid race conditions.
Memory management in C++ doesn't make the language hard, it makes it fragile, their is a big difference. The concepts behind memory management really aren't rocket science, it's the kind of thing you can learn in an afternoon.
Yes, there is a way, and it was actually submitted to the standard library:
This change makes it so that Weak::new() allocates no memory at all. Instead, it is created with a null pointer. The only things done with a Weak are trying to upgrade, cloning, and dropping, meaning there are very few places that the code actually needs to check if the pointer is null.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With