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_countrequirements so it is allowed to work with strong counter = 0. this will allow above libraries to be implemented. Not sure aboutArcat this time because ofAcquire/Releaseon 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. UniqueRcfrom RustForLinux project. It does provide ability for delayed initialization but doesn't allowWeakreferences creation. It also doesn't use stdRc/Arcso mostly out of question
Edited: typos, more alternatives