You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a tracking issue for UniqueRc and UniqueArc, as discussed in rust-lang/libs-team#90.
This feature supports creating cyclic data structures by creating an RC that is guaranteed to be uniquely owned. The uniquely owned RC can then be converted to a regular RC. While uniquely owned, we can freely modify the contents of the UniqueRc/UniqueArc (i.e. they implement DerefMut). Weak pointers to the object can be made, but upgrading these will fail until it has been converted into a regular RC.
Public API
let rc = UniqueRc::new(42);let weak = UniqueRc::downgrade(&rc);assert!(weak.upgrade().is_none());let _rc = UniqueRc::into_rc(rc);assert_eq!(*weak.upgrade().unwrap(),42);
I wonder whether it would be desirable to also add a fallible method which converts the other way, from an Rc<T> back into UniqueRc<T> provided there is only one strong reference, as part of this ticket? Has this been considered, and if so, are there any drawbacks?
Reacted by Finn Bear, kennytm, Eric Shimizu Karbstein, yuki, Stepan Koltsov, krakow10 and Vitaly Shukela
I wonder whether it would be desirable to also add a fallible method which converts the other way, from an Rc<T> back into UniqueRc<T> provided there is only one strong reference, as part of this ticket? Has this been considered, and if so, are there any drawbacks?
Good question. I think it'd help to see a concrete use case for that feature. I'm curious how often in practice you can have enough confidence that an Rc is uniquely owned so that it would make sense to convert it into a UniqueRc. Maybe for some kind of manual deinitialization process? Or maybe there are programs that have a structure that is shared for a phase of the program (e.g. spawn a bunch of threads to process it in parallel) and then goes back to being a single owner (e.g. the threads all shut down after their work is finished).
I see one major downside here (at least from my usecases). Most of the time when I need cyclic dependencies - it's when they should be em... cyclic.
I assume that idea for using UniqueRc for circular dependencies is following:
mod child {pubstructChild{parent:Weak<super::Parent>,}implChild{pubfnnew(parent:Weak<super::Parent>) -> Option<UniqueRc<Self>>{Some(Self{
parent,})}}}structParent{child:Rc<child::Child>,}implParent{fnnew() -> Option<Rc<Self>>{let child = child::Child::new(Weak::new())?;Rc::new_cyclic(|weak| {
child.parent = weak.clone();// ERROR: parent cannot be updated, field is privateSelf{child:UniqueRc::into_rc(child),}})}}
As you can see it doesn't help us in such case. And exposing parent field is not always desirable.
Another problem here is that it is almost impossible to update parent field if it is propagted to the children of the child and so on. Also by this time they are most probably were already converted from UniqueRc to Rc.
Maybe idea is to use it other way around:
fnnew() -> Option<Rc<Self>>{let rc = UnqueRc::new(Self{/* no way to initialize without client instance */});
rc.child = child::Child::new(UniqueRc::downgrade(&rc))?;Some(UniqueRc::into_rc(rc))}
But as you can see we can't even create Parent this way... We could wrap child in Option, but it will have huge impact on the usability of this field. So basically I don't see how it should help with circular dependencies in non-trivial cases.
I like the idea of UniqueRc and it has its place but IMHO not as a solution for circular dependencies.
I'd much rather suggest adding something like MaybeRc<T> (naming similar to MaybeUninit) that can be later materialized into Rc<T>. This would solve all problems with current cyclic dependencies that at very least I see in my usecases.
I'd much rather suggest adding something like MaybeRc<T> (naming similar to MaybeUninit)
FYI MaybeUninit is named like this because it could be uninitialized, but not necessarily. Your proposed MaybeRc<T> instead is always uninitialized because the moment you initialize it you have to consume it, getting back a Rc. So it has the Uninit part of MaybeUninit, but not the Maybe. UninitRc/EmptyRc seem a more fitting name to me.
Reacted by Jacob Lifshay, Rostyslav Lesovyi and Hytak
I wonder whether it would be desirable to also add a fallible method which converts the other way, from an Rc<T> back into UniqueRc<T> provided there is only one strong reference, as part of this ticket? Has this been considered, and if so, are there any drawbacks?
Good question. I think it'd help to see a concrete use case for that feature. I'm curious how often in practice you can have enough confidence that an Rc is uniquely owned so that it would make sense to convert it into a UniqueRc. Maybe for some kind of manual deinitialization process? Or maybe there are programs that have a structure that is shared for a phase of the program (e.g. spawn a bunch of threads to process it in parallel) and then goes back to being a single owner (e.g. the threads all shut down after their work is finished).
I have a Yew web application that shares state via an Rc to avoid cloning large structs. I currently use a hacky workaround which was later pointed out to be unsound. Converting an Rc to a UniqueRc is exactly what I need, as the structure of my program guarantees that the strong ref count will be 1 when I need to do mutations.
Comparing what is implemented here with the original proposal, there seems to be one downside: with UniqueRc, one has to first set up the Gadget in an invalid state, and then close the loop. Doesn't that share the downsides of what was described in the "Multi-phase Construction" proposal in the ACP?
Reacted by Rostyslav Lesovyi, Giacomo Stevanato, Travers Biddle and kennytm
I don't know if this is helpful, but I have a crate genrc which adds the same feature in a replacement Rc type (along with the ability to "project" an Rc to a contained subobject, like C++'s shared_ptr.) It uses the names RcBox / ArcBox for the unique variants, which I copied from the crate rc-box.
Maybe I don't understand something about this API, but why can't an Rc be (optionally) converted to a UniqueRc when the strong count is 1? Same thing concerning Arc -> UniqueArc
Maybe I don't understand something about this API, but why can't an Rc be (optionally) converted to a UniqueRc when the strong count is 1? Same thing concerning Arc -> UniqueArc
Activity
[-]Tracking Issue for XXX[/-][+]Tracking Issue for `UniqueRc`/`UniqueArc`[/+]programmerjake commentedon Jun 12, 2023
eholk commentedon Jun 12, 2023
ebkalderon commentedon Jun 29, 2023
I wonder whether it would be desirable to also add a fallible method which converts the other way, from an
Rc<T>back intoUniqueRc<T>provided there is only one strong reference, as part of this ticket? Has this been considered, and if so, are there any drawbacks?eholk commentedon Jun 29, 2023
Good question. I think it'd help to see a concrete use case for that feature. I'm curious how often in practice you can have enough confidence that an
Rcis uniquely owned so that it would make sense to convert it into aUniqueRc. Maybe for some kind of manual deinitialization process? Or maybe there are programs that have a structure that is shared for a phase of the program (e.g. spawn a bunch of threads to process it in parallel) and then goes back to being a single owner (e.g. the threads all shut down after their work is finished).camsteffen commentedon Jun 30, 2023
Name idea:
RootRcAs I understand it, this struct is the "root" owner of a ref-counted thing, off of which you can "branch off"
WeakRc's.tgross35 commentedon Jun 30, 2023
At some point you may want to add support for the allocator API (after #89132 lands in a few hours)
MatrixDev commentedon Jul 19, 2023
I got here from rust-lang/libs-team#90 so maybe I'm missing something.
I see one major downside here (at least from my usecases). Most of the time when I need cyclic dependencies - it's when they should be em... cyclic.
I assume that idea for using
UniqueRcfor circular dependencies is following:As you can see it doesn't help us in such case. And exposing
parentfield is not always desirable.Another problem here is that it is almost impossible to update
parentfield if it is propagted to the children of the child and so on. Also by this time they are most probably were already converted fromUniqueRctoRc.Maybe idea is to use it other way around:
But as you can see we can't even create
Parentthis way... We could wrap child inOption, but it will have huge impact on the usability of this field. So basically I don't see how it should help with circular dependencies in non-trivial cases.I like the idea of
UniqueRcand it has its place but IMHO not as a solution for circular dependencies.I'd much rather suggest adding something like
MaybeRc<T>(naming similar toMaybeUninit) that can be latermaterializedintoRc<T>. This would solve all problems with current cyclic dependencies that at very least I see in my usecases.SkiFire13 commentedon Jul 20, 2023
FYI
MaybeUninitis named like this because it could be uninitialized, but not necessarily. Your proposedMaybeRc<T>instead is always uninitialized because the moment you initialize it you have to consume it, getting back aRc. So it has theUninitpart ofMaybeUninit, but not theMaybe.UninitRc/EmptyRcseem a more fitting name to me.MatrixDev commentedon Jul 21, 2023
I agree that
MaybeRcis not the best name but don't likeEmptyRceither. MaybeLateRcorLazyRc?finnbear commentedon Jul 29, 2023
I have a Yew web application that shares state via an
Rcto avoid cloning large structs. I currently use a hacky workaround which was later pointed out to be unsound. Converting anRcto aUniqueRcis exactly what I need, as the structure of my program guarantees that the strong ref count will be 1 when I need to do mutations.RalfJung commentedon Sep 5, 2023
(Mirroring my comment from the closed ACP)
Comparing what is implemented here with the original proposal, there seems to be one downside: with UniqueRc, one has to first set up the Gadget in an invalid state, and then close the loop. Doesn't that share the downsides of what was described in the "Multi-phase Construction" proposal in the ACP?
21 remaining items
Add `Cyclic` to help build cyclic structures
Implement RcUninit (rust-lang#112566)
Implement RcUninit (rust-lang#112566)
jpetkau commentedon Jun 30, 2025
I don't know if this is helpful, but I have a crate
genrcwhich adds the same feature in a replacementRctype (along with the ability to "project" an Rc to a contained subobject, like C++'sshared_ptr.) It uses the namesRcBox/ArcBoxfor the unique variants, which I copied from the craterc-box.LeoPloutno commentedon Nov 21, 2025
Maybe I don't understand something about this API, but why can't an
Rcbe (optionally) converted to aUniqueRcwhen the strong count is 1? Same thing concerningArc -> UniqueArczachs18 commentedon Nov 21, 2025
If I understand what you're asking, that's what this other ACP is for: rust-lang/libs-team#608 . It has been approved (rust-lang/libs-team#608 (comment) ), but not yet implemented.
Rollup merge of rust-lang#162733 - maxdexh:unique-arc-pub-apis, r=nia-e
Rollup merge of rust-lang#162733 - maxdexh:unique-arc-pub-apis, r=nia-e
Rollup merge of rust-lang#162733 - maxdexh:unique-arc-pub-apis, r=nia-e
Rollup merge of rust-lang#162733 - maxdexh:unique-arc-pub-apis, r=nia-e
Unrolled build for #162733 in rollup 163002
Rollup merge of #162733 - maxdexh:unique-arc-pub-apis, r=nia-e
Rollup merge of #162733 - maxdexh:unique-arc-pub-apis, r=nia-e