---
title: "21.5 Rc<T>"
description: "The reference-counted smart pointer for shared ownership: many owners of one value, freed when the last one drops, with clone-the-handle semantics."
url: "https://learnrust.net/chapter-21/rc/"
last_updated: "2026-06-13"
---

# 21.5 Rc<T>

Since chapter 8, every value has had exactly one owner. That single-owner rule is what makes Rust's memory management so predictable: there's always precisely one value responsible for cleaning up, and when it goes, the value goes. But some data structures genuinely need a value to be owned by *several* things at once. Picture a graph where two nodes both point at a shared third node: who owns the third? Neither, and both. **`Rc<T>`**, the *reference-counted* smart pointer, is Rust's answer to shared ownership.

## The problem single ownership can't express

Try to build a cons list (lesson [21.2](https://learnrust.net/chapter-21/box/)) where two lists share a common tail:

```rust
enum List {
    Cons(i32, Box<List>),
    Nil,
}
use List::{Cons, Nil};

fn main() {
    let shared = Cons(3, Box::new(Nil));
    let a = Cons(1, Box::new(shared));
    let b = Cons(2, Box::new(shared));  // error: shared already moved into a
}
```

We want both `a` and `b` to end in `shared`. But `Box` is single ownership: building `a` *moves* `shared` into it (chapter 8), so the line building `b` fails, `shared` is already gone. The error is the familiar E0382 "use of moved value." `Box` can't do this, because the whole design of `Box` is one owner. We need a smart pointer that allows many.

## Rc: shared ownership by counting

`Rc<T>` (the name is "reference counted") holds a value plus a count of how many owners there currently are. Cloning an `Rc` doesn't copy the value, it bumps the count and hands back another handle to the *same* value. When an `Rc` handle is dropped, the count drops by one; when the count reaches zero, and only then, the value is freed. Here's the shared-tail list, working:

```rust
use std::rc::Rc;

enum List {
    Cons(i32, Rc<List>),
    Nil,
}
use List::{Cons, Nil};

fn main() {
    let shared = Rc::new(Cons(3, Rc::new(Nil)));
    let a = Cons(1, Rc::clone(&shared));
    let b = Cons(2, Rc::clone(&shared));

    println!("built two lists sharing a tail");
}
```

```
built two lists sharing a tail
```

`Rc::new` wraps the shared tail. `Rc::clone(&shared)` makes a second owner for `a` and a third for `b`, each a cheap handle to the same heap-allocated `Cons(3, ...)`. The tail isn't copied; all three `Rc`s point at one value, and it stays alive as long as any of them does. Single ownership couldn't express "this value belongs to both lists"; `Rc` can.

> **Rc::clone is cheap, and that's why it's spelled out**
>
> `Rc::clone(&shared)` only increments a counter and copies a pointer, it does *not* deep-copy the data, so it's fast no matter how big the value is. You could write `shared.clone()` and it would do the same thing, but the convention is `Rc::clone(&shared)` precisely to make it visually distinct from an expensive deep `.clone()`. When you scan code for performance, `Rc::clone` reads as "cheap, just another owner," while a plain `.clone()` might be copying a whole `Vec`. The explicit form is a readability choice, not a different operation.

## Watching the count

You can read the current count with `Rc::strong_count`. Watching it rise and fall makes the mechanism concrete:

```rust
use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("shared data"));
    println!("count after creating a: {}", Rc::strong_count(&a));

    let b = Rc::clone(&a);
    println!("count after b: {}", Rc::strong_count(&a));

    {
        let c = Rc::clone(&a);
        println!("count after c: {}", Rc::strong_count(&a));
    }   // c goes out of scope here

    println!("count after c's scope: {}", Rc::strong_count(&a));
}
```

```
count after creating a: 1
count after b: 2
count after c: 3
count after c's scope: 2
```

The count climbs to 3 as `b` and `c` are cloned, then falls back to 2 when `c` leaves its inner scope and is dropped. When `main` ends, `b` then `a` drop, the count hits 0, and the `String` is finally freed. This is `Drop` (last lesson) doing the bookkeeping: each `Rc`'s `drop` decrements the count, and the one that takes it to zero frees the data. Nobody decides "I'll be the one to free it"; the counting decides, automatically and exactly once.

## Rc is for sharing reads, single-threaded

Two limits define where `Rc` fits. First, `Rc<T>` gives you shared *immutable* access: you can have many owners reading the value, but you can't mutate through an `Rc` (that would violate chapter 9's many-readers-or-one-writer rule, several owners means several readers). When you need shared data that's *also* mutable, you combine `Rc` with the interior mutability of the next lesson, `Rc<RefCell<T>>`. Second, `Rc` is for **single-threaded** code only. Its counter isn't safe to update from multiple threads at once, so the compiler won't let an `Rc` cross a thread boundary. Chapter 22 introduces `Arc<T>`, the atomically-counted sibling, for sharing across threads; `Rc` is its lighter, single-threaded version.

> **Best practice**
>
> Reach for `Rc<T>` only when you genuinely need multiple owners of the same data and can't determine a single clear owner, typically in tree or graph structures where nodes are shared. Most code doesn't need it: a single owner with borrows (`&T`) is simpler, cheaper, and checked at compile time. `Rc` trades a little runtime cost (the counting) and gives up compile-time single-ownership clarity in exchange for shared ownership. Use it when sharing is the actual requirement, not by default.

## Quiz time

**Question #1**

What does `Rc::clone` do, and how does it differ from a normal `.clone()` that deep-copies?

<details class="solution">
<summary>Show solution</summary>

`Rc::clone` increments the reference count and returns another handle to the *same* underlying value, it copies only a pointer and bumps a counter, not the data, so it's cheap regardless of the value's size. A normal deep `.clone()` (on, say, a `Vec`) duplicates the whole value. The two are written differently by convention (`Rc::clone(&x)` vs `x.clone()`) specifically to flag the `Rc` version as cheap shared-ownership rather than an expensive copy.

</details>

**Question #2**

When is the value inside an `Rc<T>` actually freed?

<details class="solution">
<summary>Show solution</summary>

When the reference count reaches zero, that is, when the *last* `Rc` handle pointing to it is dropped. Each `Rc`'s `drop` decrements the count; the drop that takes it to 0 frees the data. As long as any owner remains, the value stays alive. This is how shared ownership cleans up: no single owner decides, the count does, automatically and exactly once.

</details>

**Question #3**

Name the two main limitations of `Rc<T>`.

<details class="solution">
<summary>Show solution</summary>

(1) It provides shared *immutable* access only, you can't mutate through an `Rc` (multiple owners would mean multiple writers, breaking chapter 9's rules); for shared mutability you combine it with `RefCell` (`Rc<RefCell<T>>`, next lesson). (2) It's single-threaded only, its non-atomic counter isn't safe across threads, so the compiler forbids sending an `Rc` between threads; `Arc<T>` (chapter 22) is the thread-safe version.

</details>

`Rc` gives many owners but only shared, read-only access. To mutate shared data, you need to bend chapter 9's borrowing rules, safely. The next lesson ([21.6](https://learnrust.net/chapter-21/refcell-and-interior-mutability/)) introduces `RefCell<T>` and *interior mutability*, moving the borrow check from compile time to run time, and finally explaining the term left dangling back in lesson 5.1.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
