Whatʼs this alleged difference between Arc::make_mut and Rc::make_mut?

I’m looking at Arc::make_mut, in particular its documentation:

Makes a mutable reference into the given Arc .

If there are other Arc or Weak pointers to the same allocation, then make_mut will create a new allocation and invoke clone on the inner value to ensure unique ownership. This is also referred to as clone-on-write.

Note that this differs from the behavior of Rc::make_mut which disassociates any remaining Weak pointers.

See also get_mut, which will fail rather than cloning.

The claim about “this differs from the behavior of Rc::make_mut which disassociates any remaining Weak pointers” is the part I don’t get. Now, comparing with the docs of Rc::make_mut

Makes a mutable reference into the given Rc .

If there are other Rc pointers to the same allocation, then make_mut will clone the inner value to a new allocation to ensure unique ownership. This is also referred to as clone-on-write.

If there are no other Rc pointers to this allocation, then Weak pointers to this allocation will be disassociated.

See also get_mut, which will fail rather than cloning.

That one does mention this “disassociation” of Weak pointers. But now… assuming that “disassociating” a Weak pointer means making it no longer upgradable, and assuming that “this differs from the behavior of Rc::make_mut” implies that Arc::make_mut doesn’t “diassociate” the Weak pointers, so those are supposed to still be upgradable, how should this work? Certainly, even by modifying the strong Arc<T> by merely “cloning” the inner value, the remaining Weak pointers pointing to the old value would immediately be without any strong pointers so they are becoming disassociated (i.e. non-upgradable) after all, right?

Looking further in the docs of Rc::make_mut… there’s an example that’s supposed to demonstrate the behavior with regards to Weak pointers:

Weak pointers will be disassociated:

use std::rc::Rc;

let mut data = Rc::new(75);
let weak = Rc::downgrade(&data);

assert!(75 == *data);
assert!(75 == *weak.upgrade().unwrap());

*Rc::make_mut(&mut data) += 1;

assert!(76 == *data);
assert!(weak.upgrade().is_none());

I mean, sure, great thing…, the example actually works, but so does the same example with Arc instead of Rc!!

Lastly, looking at the source, it clearly doesn’t do any cloning at all (despite promising so in the docs) in case of only Weak pointers existing next to the single strong Arc we have. For good measure, compare with the source code for the Rc variant. Looks like there’s a strong similarity between the two functions. I fail to see any difference at all regarding Weak pointers.


In order to keep my sanity here, please anyone be so kind to either point out what I’m missing or that the docs are, in fact, inaccurate (i.e. wrong).

7 Likes

" Weak itself makes no guarantees about the value still being present." I guess since you don't use data any more(Arc::make_mut makes a clone of data), data itself is dropped. So you get None calling upgrade on weak.

I've made a few changes on your code.Rust Playground

I concur that the docs are wrong. I'm sort of impressed they've persisted this long:

It's had incorrect wording since stabilization as far as I can tell, though it's been reworded some number of times.

Initially introduced here. As far as I can tell from skimming the tracking issue, the comment reflects the original stabilization plan (in at least some people's minds) but not the ultimate implementation (which already existed). (Note that the method used to be called make_unique.)

Edit: And I think this is where the change in behavior happened. And I also think it used to rely on is_unique at some point, which was a check against the counts (one strong no weak), but I didn't keep those tabs open so I can't cite them. (It's all pre-stabilization history anyway.)

1 Like

What do you mean by “don't use data any more”? There’s nothing else I could do to better “use data” in order to explore the behavior of the (apparently incorrectly) documented case of when there are other Weak but aren’t other Arc references to the same allocation.

With the effect of data no longer being the only strong reference. Which is then still hitting the case “If there are other Arc […] pointers to the same allocation” in the docs, but also correspondingly in the Rc world it would hit the “If there are other Arc or Weak pointers to the same allocation” case; so it shouldn’t make a difference to Rc’s behavior anymore even judging by the docs; and indeed, your modified example doesn’t present a difference between Arc::make_mut and Rc::make_mut: Translating it back to Rc shows the same behavior. By the way it’s not really “my code”; as I mentioned, it’s just one of the example from the documentation.

Back to the first thing I already quoted, in particular

well… but it doesn’t. This is the point I’m debating (among other things). While your changes to the code do lead to an observable .clone() call, the original code doesn’t clone the data all; which is expected, judging by the source code of Arc::make_mut, but contradicts the documentation, something I’ve already mentioned

Alright, lets fix it then ^^ (#88156).

5 Likes

Thanks for the thorough correction. I admit I replied quite uncarefully. :sob:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.