RustArena

Step 4 of 7

Rc<T>: shared ownership

Ownership says every value has exactly one owner. Most of the time that's what you want. But sometimes several parts of a program need to own the same value, and there's no obvious one that lives longest: a song that's on several playlists, a config shared by several services, a node in a graph with several incoming edges.

Rc<T> (reference counted) allows that. Rc::new(value) puts the value on the heap together with a counter. Rc::clone(&rc) makes another pointer to the same value and adds 1 to the strong count. Dropping an Rc subtracts 1. When the count reaches 0, the value is dropped.

use std::rc::Rc;

struct Song {
    title: String,
}

fn main() {
    let song = Rc::new(Song { title: String::from("Crab Rave") });
    println!("count after new: {}", Rc::strong_count(&song));

    let workout = vec![Rc::clone(&song)];
    {
        let road_trip = vec![Rc::clone(&song)];
        println!("count with two playlists: {}", Rc::strong_count(&song));
        println!("road trip plays {}", road_trip[0].title);
    }
    println!("count after the road trip: {}", Rc::strong_count(&song));
    println!("workout plays {}", workout[0].title);
}
count after new: 1
count with two playlists: 3
road trip plays Crab Rave
count after the road trip: 2
workout plays Crab Rave

Rc is read-only and single-threaded

An Rc<T> only hands out shared references (&T). With several owners, letting any one of them mutate would break the "one writer" rule, so this doesn't compile:

use std::rc::Rc;

fn main() {
    let scores = Rc::new(vec![1, 2]);
    scores.push(3);
    println!("{scores:?}");
}
error[E0596]: cannot borrow data in an `Rc` as mutable

To change shared data, combine Rc with RefCell (next two steps). And Rc updates its counter without any thread synchronization, so the compiler won't let you send it to another thread. Its thread-safe twin is Arc<T> (atomically reference counted), coming in the concurrency modules.

Common mistakes

Your task

Fill the blanks: make a and b share the same config with Rc::clone, and print the strong count. The program prints:

owners: 3
a sees retries = 5
owners after a is gone: 2

Challenge 2

Two lists can share the same tail when the tail is held in an Rc. Finish the program:

  1. shared is the list 10 -> 20.
  2. a is 1 -> shared and b is 2 -> shared (both point at the same shared, using Rc::clone).
  3. Write fn sum(list: &List) -> i32.

The program must print exactly:

sum a = 31
sum b = 32
shared owners = 3

Challenge 3

A tournament player can be on several teams. Write:

Previous: Deref and Drop: what makes a pointer smart Next: RefCell and Cell: changing data behind &