Sometimes you need several references to an immutable value at the same time, this is also called shared ownership. The pointer type Box<T> can't help us out here, because this type has a single owner by definition. For this, Rust provides the generic reference counted box Rc<T>, where multiple references can share the same resource. The std::rc module provides a way to share ownership of the same value between different Rc pointers; the value remains alive as long as there is least one pointer referencing it.
In the following example, we have Aliens that have a number of tentacles. Each Tentacle is also a struct instance and has to indicate to which Alien it belongs; besides that it has other properties like a degree of poison. A first attempt at this could be the following code which however does not compile:
// see Chapter 7/code/refcount_not_good.rs): struct Alien { name: String, n_tentacles: u8 } struct Tentacle { poison: u8, owner: Alien } fn main() { let dhark = Alien { name: "Dharkalen".to_string(), n_tentacles: 7 }; // defining dhark's tentacles: for i in 0u8..dhark.n_tentacles { Tentacle { poison: i * 3, owner: dhark }; // <- error! } }
The compiler gives a following error for the line in the for loop:
error: use of moved value 'dhark' - move occurs because `dhark` has type `Alien`, which does not implement the `Copy` trait
Each Alien Tentacle when it is defined seemingly tries to make a copy of the Alien instance as its owner, which would make no sense and is not allowed.
The correct version defines the owner in the Tentacle struct to have the type Rc<Alien>:
// see code in Chapter 7/code/refcount.rs use std::rc::Rc; #[derive(Debug)] struct Alien { name: String, n_tentacles: u8 } #[derive(Debug)] struct Tentacle { poison: u8, owner: Rc<Alien> } fn main() { let dhark = Alien { name: "Dharkalen".to_string(), n_tentacles: 7 }; let dhark_master = Rc::new(dhark); for i in 0u8..dhark_master.n_tentacles { let t = Tentacle { poison: i * 3, owner: dhark_master.clone() }; println!("{:?}", t); } }
This prints the following output:
Tentacle {poison: 0, owner: Alien{name: "Dharkalen", n_tentacles: 7}}
Tentacle {poison: 3, owner: Alien{name: "Dharkalen", n_tentacles: 7}}
Tentacle {poison: 6, owner: Alien{name: "Dharkalen", n_tentacles: 7 }}
Tentacle {poison: 18, owner: Alien{name: "Dharkalen", n_tentacles: 7}}
We envelop our Alien instance in an Rc<T> type with Rc::new(dhark). Applying the clone() method on this Rc object provides each Tentacle with its own reference to the Alien object. Note that clone() here copies the Rc pointer, not the Alien struct. We also annotate the structs with #[derive(Debug)], so that we can print out their instances through a println!("{:?}", t);.
If we want mutability inside our Rc type, we have to use either a Cell pointer if the value implements the Copy trait, or a RefCell pointer otherwise. Both these smart pointers are found in the std:cell module.
The Rc pointer type can however only be used inside one thread of execution only. If you need shared ownership across multiple threads, you need to use the Arc<T> pointer (atomic reference counted box), which is the thread-safe counterpart of Rc (see Chapter 9</span>, Concurrency - Coding for Multicore Execution in the section Atomic reference counting).