We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Smart Pointers and Interior Mutability step 6 of 26
Inside `Rc`: `strong_count`, `ptr_eq`, `get_mut`, `try_unwrap`
Interpret a small script over a set of named Rc<i64> handles and return the
log it produces.
pub fn ownership_trace(script: Vec<String>) -> Vec<String>
Keep the handles in a BTreeMap<String, Rc<i64>>. Each line is one command;
each command appends exactly one line to the log.
| command | effect | log line |
|---|---|---|
new <n> <v> |
create a fresh Rc<i64> named <n> |
<n>=1 |
clone <src> <dst> |
<dst> becomes another handle to <src>‘s value |
<dst>=<strong count> |
count <n> |
— |
<n>=<strong count> |
read <n> |
— |
<n> is <value> |
drop <n> |
forget the handle <n> |
drop <n> -> <count that remains> |
get_mut <n> <d> |
add <d> to the value if <n> is the only handle |
get_mut <n> ok / get_mut <n> shared |
unwrap <n> |
Rc::try_unwrap — take the value if <n> is the only handle |
unwrap <n> ok <value> / unwrap <n> shared |
into_inner <n> |
Rc::into_inner — same question, different answer shape |
into_inner <n> ok <value> / into_inner <n> none |
ptr_eq <a> <b> |
compare both ways |
ptr_eq <a> <b> ptr=<bool> val=<bool> |
When a named handle does not exist, log the same shape with ? in place of
the number: <n>=?, <n> is ?, drop <n> -> ?, get_mut <n> ?,
unwrap <n> ?, into_inner <n> ?, ptr_eq <a> <b> ?. Any line that is not
a command at all logs ?.
Two details that decide several test cases:
-
unwrapon a shared handle gets theRcback in theErr, and the named handle must survive — put it back in the map. -
into_inneron a shared handle returnsNoneand consumes the handle anyway. That handle is gone; the count of the others goes down.
Why these are associated functions
You call Rc::strong_count(&a), not a.strong_count(). That is not an
oversight and it is not stylistic. An Rc<T> derefs to T, so a.foo()
resolves to T‘s foo — and if Rc had inherent methods, every one of them
would shadow a method of the same name on whatever you wrapped. Imagine
Rc<MyType> where MyType::clone quietly stopped being reachable.
So Rc‘s own operations are all spelled as associated functions taking the
handle explicitly, leaving the entire method namespace to T. The same
applies to Box::leak, Rc::ptr_eq, Rc::get_mut — and once you know the
reason, the API stops feeling arbitrary. It is the price of Deref, and
every smart pointer pays it.
ptr_eq versus ==
Rc::ptr_eq(&a, &b) asks “are these the same allocation?”. a == b asks
“do these values compare equal?”. Those are different questions and one
test case exists purely to separate them: two Rcs created independently from
the same number are ptr_eq == false and == true.
Identity is the question you want when you are asking whether two handles into a graph refer to the same node. Equality is the question you want when comparing contents. Reaching for the wrong one produces bugs that are very hard to see, because in most of your test data they agree.
“Can I ever mutate through an Rc without a RefCell?”
Yes — when you are provably the only owner. That is what the last three commands are about, and the three answers differ in useful ways:
-
Rc::get_mut(&mut rc) -> Option<&mut T>—Somewhen the count is 1, so you keep theRcand mutate in place. -
Rc::try_unwrap(rc) -> Result<T, Rc<T>>— consumes the handle.Ok(value)when it was the last one;Err(rc)hands it back untouched when it was not. -
Rc::into_inner(rc) -> Option<T>— consumes the handle either way, and reports failure asNone. It istry_unwrapfor the case where you did not want the handle back.
Notice the shape of the starter’s bug:
error[E0308]: mismatched types
| match Rc::get_mut(rc) {
| ^ types differ in mutability
= note: expected mutable reference `&mut Rc<_, _>`
found reference `&Rc<i64>`
get_mut needs &mut on the handle, not on the value. That is the whole
trick: an exclusive borrow of the handle plus a strong count of 1 together
prove nobody else can be looking, which is exactly the proof &mut T requires.
Change one call from get to get_mut and it compiles.
One trap worth knowing before it bites you in three items’ time: a live
Weak blocks get_mut even when the strong count is 1. You get a
mystifying None with a single handle in sight. try_unwrap and
into_inner, by contrast, only care about strong references.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.