Skip to content

Commit 6d60956

Browse files
committed
auto merge of #15251 : AlisdairO/rust/master, r=alexcrichton
I've seen quite a bit of confusion around regarding the use of Rc, so I thought I'd take a stab at adding some examples.
2 parents efebeec + 88008b3 commit 6d60956

File tree

1 file changed

+134
-9
lines changed

1 file changed

+134
-9
lines changed

src/liballoc/rc.rs

+134-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -10,16 +10,141 @@
1010

1111
/*! Task-local reference-counted boxes (`Rc` type)
1212
13-
The `Rc` type provides shared ownership of an immutable value. Destruction is deterministic, and
14-
will occur as soon as the last owner is gone. It is marked as non-sendable because it avoids the
15-
overhead of atomic reference counting.
13+
The `Rc` type provides shared ownership of an immutable value. Destruction is
14+
deterministic, and will occur as soon as the last owner is gone. It is marked
15+
as non-sendable because it avoids the overhead of atomic reference counting.
1616
17-
The `downgrade` method can be used to create a non-owning `Weak` pointer to the box. A `Weak`
18-
pointer can be upgraded to an `Rc` pointer, but will return `None` if the value has already been
19-
freed.
17+
The `downgrade` method can be used to create a non-owning `Weak` pointer to the
18+
box. A `Weak` pointer can be upgraded to an `Rc` pointer, but will return
19+
`None` if the value has already been freed.
2020
21-
For example, a tree with parent pointers can be represented by putting the nodes behind `Strong`
22-
pointers, and then storing the parent pointers as `Weak` pointers.
21+
For example, a tree with parent pointers can be represented by putting the
22+
nodes behind strong `Rc` pointers, and then storing the parent pointers as
23+
`Weak` pointers.
24+
25+
26+
## Examples
27+
28+
Consider a scenario where a set of Gadgets are owned by a given Owner. We want
29+
to have our Gadgets point to their Owner. We can't do this with unique
30+
ownership, because more than one gadget may belong to the same Owner. Rc
31+
allows us to share an Owner between multiple Gadgets, and have the Owner kept
32+
alive as long as any Gadget points at it.
33+
34+
```rust
35+
use std::rc::Rc;
36+
37+
struct Owner {
38+
name: String
39+
// ...other fields
40+
}
41+
42+
struct Gadget {
43+
id: int,
44+
owner: Rc<Owner>
45+
// ...other fields
46+
}
47+
48+
fn main() {
49+
// Create a reference counted Owner.
50+
let gadget_owner : Rc<Owner> = Rc::new(
51+
Owner { name: String::from_str("Gadget Man") }
52+
);
53+
54+
// Create Gadgets belonging to gadget_owner. To increment the reference
55+
// count we clone the Rc object.
56+
let gadget1 = Gadget { id: 1, owner: gadget_owner.clone() };
57+
let gadget2 = Gadget { id: 2, owner: gadget_owner.clone() };
58+
59+
drop(gadget_owner);
60+
61+
// Despite dropping gadget_owner, we're still able to print out the name of
62+
// the Owner of the Gadgets. This is because we've only dropped the
63+
// reference count object, not the Owner it wraps. As long as there are
64+
// other Rc objects pointing at the same Owner, it will stay alive. Notice
65+
// that the Rc wrapper around Gadget.owner gets automatically dereferenced
66+
// for us.
67+
println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name);
68+
println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name);
69+
70+
// At the end of the method, gadget1 and gadget2 get destroyed, and with
71+
// them the last counted references to our Owner. Gadget Man now gets
72+
// destroyed as well.
73+
}
74+
```
75+
76+
If our requirements change, and we also need to be able to traverse from
77+
Owner->Gadget, we will run into problems: an Rc pointer from Owner->Gadget
78+
introduces a cycle between the objects. This means that their reference counts
79+
can never reach 0, and the objects will stay alive: a memory leak. In order to
80+
get around this, we can use `Weak` pointers. These are reference counted
81+
pointers that don't keep an object alive if there are no normal `Rc` (or
82+
*strong*) pointers left.
83+
84+
Rust actually makes it somewhat difficult to produce this loop in the first
85+
place: in order to end up with two objects that point at each other, one of
86+
them needs to be mutable. This is problematic because Rc enforces memory
87+
safety by only giving out shared references to the object it wraps, and these
88+
don't allow direct mutation. We need to wrap the part of the object we wish to
89+
mutate in a `RefCell`, which provides *interior mutability*: a method to
90+
achieve mutability through a shared reference. `RefCell` enforces Rust's
91+
borrowing rules at runtime. Read the `Cell` documentation for more details on
92+
interior mutability.
93+
94+
```rust
95+
use std::rc::Rc;
96+
use std::rc::Weak;
97+
use std::cell::RefCell;
98+
99+
struct Owner {
100+
name: String,
101+
gadgets: RefCell<Vec<Weak<Gadget>>>
102+
// ...other fields
103+
}
104+
105+
struct Gadget {
106+
id: int,
107+
owner: Rc<Owner>
108+
// ...other fields
109+
}
110+
111+
fn main() {
112+
// Create a reference counted Owner. Note the fact that we've put the
113+
// Owner's vector of Gadgets inside a RefCell so that we can mutate it
114+
// through a shared reference.
115+
let gadget_owner : Rc<Owner> = Rc::new(
116+
Owner {
117+
name: "Gadget Man".to_string(),
118+
gadgets: RefCell::new(Vec::new())
119+
}
120+
);
121+
122+
// Create Gadgets belonging to gadget_owner as before.
123+
let gadget1 = Rc::new(Gadget{id: 1, owner: gadget_owner.clone()});
124+
let gadget2 = Rc::new(Gadget{id: 2, owner: gadget_owner.clone()});
125+
126+
// Add the Gadgets to their Owner. To do this we mutably borrow from
127+
// the RefCell holding the Owner's Gadgets.
128+
gadget_owner.gadgets.borrow_mut().push(gadget1.clone().downgrade());
129+
gadget_owner.gadgets.borrow_mut().push(gadget2.clone().downgrade());
130+
131+
// Iterate over our Gadgets, printing their details out
132+
for gadget_opt in gadget_owner.gadgets.borrow().iter() {
133+
134+
// gadget_opt is a Weak<Gadget>. Since weak pointers can't guarantee
135+
// that their object is still alive, we need to call upgrade() on them
136+
// to turn them into a strong reference. This returns an Option, which
137+
// contains a reference to our object if it still exists.
138+
let gadget = gadget_opt.upgrade().unwrap();
139+
println!("Gadget {} owned by {}", gadget.id, gadget.owner.name);
140+
}
141+
142+
// At the end of the method, gadget_owner, gadget1 and gadget2 get
143+
// destroyed. There are now no strong (Rc) references to the gadgets.
144+
// Once they get destroyed, the Gadgets get destroyed. This zeroes the
145+
// reference count on Gadget Man, so he gets destroyed as well.
146+
}
147+
```
23148
24149
*/
25150

0 commit comments

Comments
 (0)