Expand description
Single-threaded reference-counting pointers. ‘Rc’ stands for ‘Reference Counted’.
The type Rc<T>
provides shared ownership of a value of type T
,
allocated in the heap. Invoking clone
on Rc
produces a new
pointer to the same allocation in the heap. When the last Rc
pointer to a
given allocation is destroyed, the value stored in that allocation (often
referred to as “inner value”) is also dropped.
Shared references in Rust disallow mutation by default, and Rc
is no exception: you cannot generally obtain a mutable reference to
something inside an Rc
. If you need mutability, put a Cell
or RefCell
inside the Rc
; see an example of mutability
inside an Rc
.
Rc
uses non-atomic reference counting. This means that overhead is very
low, but an Rc
cannot be sent between threads, and consequently Rc
does not implement Send
. As a result, the Rust compiler
will check at compile time that you are not sending Rc
s between
threads. If you need multi-threaded, atomic reference counting, use
sync::Arc
.
The downgrade
method can be used to create a non-owning
Weak
pointer. A Weak
pointer can be upgrade
d
to an Rc
, but this will return None
if the value stored in the allocation has
already been dropped. In other words, Weak
pointers do not keep the value
inside the allocation alive; however, they do keep the allocation
(the backing store for the inner value) alive.
A cycle between Rc
pointers will never be deallocated. For this reason,
Weak
is used to break cycles. For example, a tree could have strong
Rc
pointers from parent nodes to children, and Weak
pointers from
children back to their parents.
Rc<T>
automatically dereferences to T
(via the Deref
trait),
so you can call T
’s methods on a value of type Rc<T>
. To avoid name
clashes with T
’s methods, the methods of Rc<T>
itself are associated
functions, called using fully qualified syntax:
use std::rc::Rc;
let my_rc = Rc::new(());
let my_weak = Rc::downgrade(&my_rc);
RunRc<T>
’s implementations of traits like Clone
may also be called using
fully qualified syntax. Some people prefer to use fully qualified syntax,
while others prefer using method-call syntax.
use std::rc::Rc;
let rc = Rc::new(());
// Method-call syntax
let rc2 = rc.clone();
// Fully qualified syntax
let rc3 = Rc::clone(&rc);
RunWeak<T>
does not auto-dereference to T
, because the inner value may have
already been dropped.
Cloning references
Creating a new reference to the same allocation as an existing reference counted pointer
is done using the Clone
trait implemented for Rc<T>
and Weak<T>
.
use std::rc::Rc;
let foo = Rc::new(vec![1.0, 2.0, 3.0]);
// The two syntaxes below are equivalent.
let a = foo.clone();
let b = Rc::clone(&foo);
// a and b both point to the same memory location as foo.
RunThe Rc::clone(&from)
syntax is the most idiomatic because it conveys more explicitly
the meaning of the code. In the example above, this syntax makes it easier to see that
this code is creating a new reference rather than copying the whole content of foo.
Examples
Consider a scenario where a set of Gadget
s are owned by a given Owner
.
We want to have our Gadget
s point to their Owner
. We can’t do this with
unique ownership, because more than one gadget may belong to the same
Owner
. Rc
allows us to share an Owner
between multiple Gadget
s,
and have the Owner
remain allocated as long as any Gadget
points at it.
use std::rc::Rc;
struct Owner {
name: String,
// ...other fields
}
struct Gadget {
id: i32,
owner: Rc<Owner>,
// ...other fields
}
fn main() {
// Create a reference-counted `Owner`.
let gadget_owner: Rc<Owner> = Rc::new(
Owner {
name: "Gadget Man".to_string(),
}
);
// Create `Gadget`s belonging to `gadget_owner`. Cloning the `Rc<Owner>`
// gives us a new pointer to the same `Owner` allocation, incrementing
// the reference count in the process.
let gadget1 = Gadget {
id: 1,
owner: Rc::clone(&gadget_owner),
};
let gadget2 = Gadget {
id: 2,
owner: Rc::clone(&gadget_owner),
};
// Dispose of our local variable `gadget_owner`.
drop(gadget_owner);
// Despite dropping `gadget_owner`, we're still able to print out the name
// of the `Owner` of the `Gadget`s. This is because we've only dropped a
// single `Rc<Owner>`, not the `Owner` it points to. As long as there are
// other `Rc<Owner>` pointing at the same `Owner` allocation, it will remain
// live. The field projection `gadget1.owner.name` works because
// `Rc<Owner>` automatically dereferences to `Owner`.
println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name);
println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name);
// At the end of the function, `gadget1` and `gadget2` are destroyed, and
// with them the last counted references to our `Owner`. Gadget Man now
// gets destroyed as well.
}
RunIf our requirements change, and we also need to be able to traverse from
Owner
to Gadget
, we will run into problems. An Rc
pointer from Owner
to Gadget
introduces a cycle. This means that their
reference counts can never reach 0, and the allocation will never be destroyed:
a memory leak. In order to get around this, we can use Weak
pointers.
Rust actually makes it somewhat difficult to produce this loop in the first
place. In order to end up with two values that point at each other, one of
them needs to be mutable. This is difficult because Rc
enforces
memory safety by only giving out shared references to the value it wraps,
and these don’t allow direct mutation. We need to wrap the part of the
value we wish to mutate in a RefCell
, which provides interior
mutability: a method to achieve mutability through a shared reference.
RefCell
enforces Rust’s borrowing rules at runtime.
use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;
struct Owner {
name: String,
gadgets: RefCell<Vec<Weak<Gadget>>>,
// ...other fields
}
struct Gadget {
id: i32,
owner: Rc<Owner>,
// ...other fields
}
fn main() {
// Create a reference-counted `Owner`. Note that we've put the `Owner`'s
// vector of `Gadget`s inside a `RefCell` so that we can mutate it through
// a shared reference.
let gadget_owner: Rc<Owner> = Rc::new(
Owner {
name: "Gadget Man".to_string(),
gadgets: RefCell::new(vec![]),
}
);
// Create `Gadget`s belonging to `gadget_owner`, as before.
let gadget1 = Rc::new(
Gadget {
id: 1,
owner: Rc::clone(&gadget_owner),
}
);
let gadget2 = Rc::new(
Gadget {
id: 2,
owner: Rc::clone(&gadget_owner),
}
);
// Add the `Gadget`s to their `Owner`.
{
let mut gadgets = gadget_owner.gadgets.borrow_mut();
gadgets.push(Rc::downgrade(&gadget1));
gadgets.push(Rc::downgrade(&gadget2));
// `RefCell` dynamic borrow ends here.
}
// Iterate over our `Gadget`s, printing their details out.
for gadget_weak in gadget_owner.gadgets.borrow().iter() {
// `gadget_weak` is a `Weak<Gadget>`. Since `Weak` pointers can't
// guarantee the allocation still exists, we need to call
// `upgrade`, which returns an `Option<Rc<Gadget>>`.
//
// In this case we know the allocation still exists, so we simply
// `unwrap` the `Option`. In a more complicated program, you might
// need graceful error handling for a `None` result.
let gadget = gadget_weak.upgrade().unwrap();
println!("Gadget {} owned by {}", gadget.id, gadget.owner.name);
}
// At the end of the function, `gadget_owner`, `gadget1`, and `gadget2`
// are destroyed. There are now no strong (`Rc`) pointers to the
// gadgets, so they are destroyed. This zeroes the reference count on
// Gadget Man, so he gets destroyed as well.
}
RunStructs
- A single-threaded reference-counting pointer. ‘Rc’ stands for ‘Reference Counted’.
- UniqueRcExperimentalA uniquely owned
Rc