I'm writing this following a discussion and suggestion given in the Upgrading "dead" Weak< UnsafeCell< MaybeUninit<T>>> to Rc<T> - #13 by matrixdev - help - The Rust Programming Language Forum thread.
What is the proposal?
In short the proposal is to split Rc/Arc::new_cyclic
function into two parts.
First part is the creation of "empty" Rc
wrapper which can provide Weak
references that cannot be upgraded at this stage.
Second part is to finalize Rc
/Arc
creation by initializing its content and allowing Weak
references to be upgraded.
Why do we need it?
Rc/Arc::new_cyclic
has a lot of limitation when it comes to error handling and async. Instead of writing try_new_cyclic
, maybe_new_cyclic
, new_cyclic_async
, try_new_cyclic_async
, maybe_new_cyclic_async
(or how many more will come up in the future) it should be easier to just separate RcBox
allocation and first strong reference creation. This is basically Rc/Arc::new_cyclic
but split in half.
Example
I'll try to give example from library I've unsuccessfully tried to implement (mainly because of the public API limitations) but the idea should be the same:
let maybe: MaybeRc<Self> = MaybeRc::new();
let weak: Weak<Self> = maybe.downgrade();
assert!(weak.upgrade().is_none());
// Child creation is async and can fail here
// Rc::new_cyclic will not work here
let child: Rc<Child> = Child::new(weak.clone()).await?;
let rc: Rc<Self> = maybe.into_rc(Self {
child,
});
assert!(weak.upgrade().is_some());
Additionally it can be easily convertd into UniqueRc
to get mutable reference for further initialization when it goes live:
let rc: UnigueRc<Self> = maybe.into_unique_rc(Self {
child,
});
assert!(weak.upgrade().is_none());
Considered alternatives?
- writing own library - doesn't work because we can't increment strong counter wich is 0
- relaxing
Rc::increment_strong_count
/Rc::decrement_strong_count
requirements so it is allowed to work with strong counter = 0. this will allow above libraries to be implemented. Not sure aboutArc
at this time because ofAcquire/Release
on atomics. - add special unsafe method to increase strong count specifically from 0 to 1
- mentioned above
UniqueRc
. but it doesn't cover this case, you can read my comment there. UniqueRc
from RustForLinux project. It does provide ability for delayed initialization but doesn't allowWeak
references creation. It also doesn't use stdRc
/Arc
so mostly out of question
Edited: typos, more alternatives