From 88008b3ad564ed2ba16c2acf4e9538f20a71f541 Mon Sep 17 00:00:00 2001 From: Alisdair Owens Date: Sun, 29 Jun 2014 16:33:42 +0100 Subject: [PATCH] Improve rustdocs for Rc, add examples --- src/liballoc/rc.rs | 143 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 9 deletions(-) diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 83cc4a0b662..d97bce39c2d 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,16 +10,141 @@ /*! Task-local reference-counted boxes (`Rc` type) -The `Rc` type provides shared ownership of an immutable value. Destruction is deterministic, and -will occur as soon as the last owner is gone. It is marked as non-sendable because it avoids the -overhead of atomic reference counting. +The `Rc` type provides shared ownership of an immutable value. Destruction is +deterministic, and will occur as soon as the last owner is gone. It is marked +as non-sendable because it avoids the overhead of atomic reference counting. -The `downgrade` method can be used to create a non-owning `Weak` pointer to the box. A `Weak` -pointer can be upgraded to an `Rc` pointer, but will return `None` if the value has already been -freed. +The `downgrade` method can be used to create a non-owning `Weak` pointer to the +box. A `Weak` pointer can be upgraded to an `Rc` pointer, but will return +`None` if the value has already been freed. -For example, a tree with parent pointers can be represented by putting the nodes behind `Strong` -pointers, and then storing the parent pointers as `Weak` pointers. +For example, a tree with parent pointers can be represented by putting the +nodes behind strong `Rc` pointers, and then storing the parent pointers as +`Weak` pointers. + + +## Examples + +Consider a scenario where a set of Gadgets are owned by a given Owner. We want +to have our Gadgets 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 Gadgets, and have the Owner kept +alive as long as any Gadget points at it. + +```rust +use std::rc::Rc; + +struct Owner { + name: String + // ...other fields +} + +struct Gadget { + id: int, + owner: Rc + // ...other fields +} + +fn main() { + // Create a reference counted Owner. + let gadget_owner : Rc = Rc::new( + Owner { name: String::from_str("Gadget Man") } + ); + + // Create Gadgets belonging to gadget_owner. To increment the reference + // count we clone the Rc object. + let gadget1 = Gadget { id: 1, owner: gadget_owner.clone() }; + let gadget2 = Gadget { id: 2, owner: gadget_owner.clone() }; + + drop(gadget_owner); + + // Despite dropping gadget_owner, we're still able to print out the name of + // the Owner of the Gadgets. This is because we've only dropped the + // reference count object, not the Owner it wraps. As long as there are + // other Rc objects pointing at the same Owner, it will stay alive. Notice + // that the Rc wrapper around Gadget.owner gets automatically dereferenced + // for us. + println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name); + println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name); + + // At the end of the method, gadget1 and gadget2 get destroyed, and with + // them the last counted references to our Owner. Gadget Man now gets + // destroyed as well. +} +``` + +If our requirements change, and we also need to be able to traverse from +Owner->Gadget, we will run into problems: an Rc pointer from Owner->Gadget +introduces a cycle between the objects. This means that their reference counts +can never reach 0, and the objects will stay alive: a memory leak. In order to +get around this, we can use `Weak` pointers. These are reference counted +pointers that don't keep an object alive if there are no normal `Rc` (or +*strong*) pointers left. + +Rust actually makes it somewhat difficult to produce this loop in the first +place: in order to end up with two objects that point at each other, one of +them needs to be mutable. This is problematic because Rc enforces memory +safety by only giving out shared references to the object it wraps, and these +don't allow direct mutation. We need to wrap the part of the object 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. Read the `Cell` documentation for more details on +interior mutability. + +```rust +use std::rc::Rc; +use std::rc::Weak; +use std::cell::RefCell; + +struct Owner { + name: String, + gadgets: RefCell>> + // ...other fields +} + +struct Gadget { + id: int, + owner: Rc + // ...other fields +} + +fn main() { + // Create a reference counted Owner. Note the fact that we've put the + // Owner's vector of Gadgets inside a RefCell so that we can mutate it + // through a shared reference. + let gadget_owner : Rc = Rc::new( + Owner { + name: "Gadget Man".to_string(), + gadgets: RefCell::new(Vec::new()) + } + ); + + // Create Gadgets belonging to gadget_owner as before. + let gadget1 = Rc::new(Gadget{id: 1, owner: gadget_owner.clone()}); + let gadget2 = Rc::new(Gadget{id: 2, owner: gadget_owner.clone()}); + + // Add the Gadgets to their Owner. To do this we mutably borrow from + // the RefCell holding the Owner's Gadgets. + gadget_owner.gadgets.borrow_mut().push(gadget1.clone().downgrade()); + gadget_owner.gadgets.borrow_mut().push(gadget2.clone().downgrade()); + + // Iterate over our Gadgets, printing their details out + for gadget_opt in gadget_owner.gadgets.borrow().iter() { + + // gadget_opt is a Weak. Since weak pointers can't guarantee + // that their object is still alive, we need to call upgrade() on them + // to turn them into a strong reference. This returns an Option, which + // contains a reference to our object if it still exists. + let gadget = gadget_opt.upgrade().unwrap(); + println!("Gadget {} owned by {}", gadget.id, gadget.owner.name); + } + + // At the end of the method, gadget_owner, gadget1 and gadget2 get + // destroyed. There are now no strong (Rc) references to the gadgets. + // Once they get destroyed, the Gadgets get destroyed. This zeroes the + // reference count on Gadget Man, so he gets destroyed as well. +} +``` */ -- 2.44.0