---
title: 'Ownership and the Memory Model'
source: 'https://academia.sh/en/courses/ownership'
language: en
updated: '2026-09-15T04:58:40+00:00'
license: 'CC BY-SA 4.0'
lessons: 14
outcomes:
    - 'Explains the ownership, borrowing and lifetime rules with their reasons'
    - "Measures by running it where a value's life ends"
    - 'Resolves a writing the borrow checker rejects with a design change'
    - 'Uses shared ownership and interior mutability tools under their conditions'
---

# Ownership and the Memory Model

In Rust a value's memory is returned at a point nobody decides later, and that point can be read from the source: the course measures every construct with one question — where does this value's life end? Moves, borrowing, slices, lifetime annotations, single ownership on the heap, reference counting and interior mutability all pass through the same frame.

## Ownership Rules

How visible in the type a value's location is, how the single-owner rule forces a move, how borrowing grants access without transferring ownership, how a slice views data without taking ownership, and what the checker that tests all of this at compile time actually guarantees.

### Stack and Heap

Source: https://academia.sh/en/courses/ownership/stack-and-heap

A fourteen-value measurement core runs and places every value's life into one of four classes — scope end, where it was moved, an explicit call, together with its container; the result is fixed at 6/3/1/4. It measures that values ending at scope end finish in the exact reverse of their declaration order, and that a heap-allocated value drops into the same ledger.

The previous course measured what a line written in source actually produces: an
expression, a statement, an item. That three-way classification determined what a line
was at the moment it ran, but it deliberately left one question unasked — how long the
value that line produces lives. This course looks at exactly that, and one single
question runs through every lesson in it: where does this value's life end?

This question has come up before, in a different context. The Concurrency course
measured how a goroutine stops — the termination of a running unit. This course applies
the same verb to a different subject: there, a running unit stopped; here, a value ends.
A unit's stopping is triggered by an outside decision (a channel closing, a context
being canceled), while a value's ending can be read straight from the source's own
structure — with no separate decision mechanism needed.

#### Four Answers

In Rust, a value's life ends in one of four forms, and these four names stay the same
throughout the course.

| Class | Definition |
|---|---|
| scope end | The value ends at the close of the block it is bound to |
| where it was moved | Ownership passed to someone else; the life ends where the new owner ends |
| an explicit call | A call written in the source ends the life early |
| together with its container | The value is inside a container; it ends when the container does |

These four classes are mutually exclusive: a value cannot end both at scope end and
where it was moved at the same time, because a moved value's old binding is already
dead — the ending happens exactly once in a value's life, through exactly one of the
four paths. The ending point is read from the source's own structure and does not
depend on a runtime decision; that is why the measurement produces not a duration or a
counter, but an ordered record — a list of which value ended when.

The Programming Fundamentals course already established the concept of scope: which
block a name appears in, and how the name becomes unreachable once that block is left.
This course does not redefine that concept; what this topic contributes is that Rust
uses scope not only as a visibility boundary but also as an **ending point**. A name
going out of scope means, in most languages, only that the name can no longer be
reached; here, the same event also means that the value the name held ends — and that
ending happens at a point known at compile time, with certainty, at runtime.

#### The Measurement Core

The core used through the rest of the course is `life_core.rs`. Fourteen separate
values are created in four sections (A, B, C, D); each value's ending is written to a
shared observation ledger, and a total per class is printed at the end.

Every section takes its own measurement the same way: before the section starts, the
ledger's current length (`a0`, `b0`, `c0`, `d0`) is recorded; once the section's scope
closes, the `new_entries` function returns only the lines added after that length. This
keeps the four sections from bleeding into each other's measurement — the three values
that end in section A never show up again in section B's measurement, because B's
starting point already takes the length after A finished. The same ledger is shared
across all four, but each section reads its own share starting from its own starting
point.

The observation ledger is held by the `Ledger` type — a shared record that lets
multiple `Trace` instances write to the same list. The internal structure behind this
sharing (`Rc<RefCell<Vec<String>>>`) is a design choice of the measurement tool; how it
works will be treated as its own subject in the Reference Counting and Interior
Mutability lessons. What matters here is only this: every `Trace` value implements the
`Drop` trait, so when a value's life ends, the compiler automatically runs a closing
code path, and that code adds the value's own name to the ledger. `Drop`'s own rules —
which types can implement it, its relationship to `Clone`/`Copy` — are not this course's
subject; the only thing measured is **where** in the source this triggering happens.

**OR1.** `Ledger` and `Trace` themselves are design choices of the measurement tool, not
a requirement of the language; what decides which class a value falls into is the
structure of the code itself — a scope closing, a move, an explicit call, or being
inside a container. The `Trace` type was written to make these four situations visible;
it is not itself one of them.

```rust
// life_core.rs — where does a value's life end: at scope end, where it was moved,
// as an explicit call, or together with its container.

use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

struct Container {
    content: Trace,
}

fn consume(_received: Trace) {
    // The value was moved in here; its life ends here when the body finishes.
}

fn read_log(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}

fn new_entries(ledger: &Ledger, previous: usize) -> String {
    let all = read_log(ledger);
    all[previous..].join(", ")
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    let mut tally: Vec<(&str, &str)> = Vec::new();

    // --- A: scope end ---
    let a0 = read_log(&ledger).len();
    {
        let _first = Trace::new("scope-1", &ledger);
        let _second = Trace::new("scope-2", &ledger);
        let _third = Trace::new("scope-3", &ledger);
    }
    println!("A scope end                    -> {}", new_entries(&ledger, a0));
    for name in ["scope-1", "scope-2", "scope-3"] {
        tally.push((name, "scope end"));
    }

    // A shadowed value also lives until scope end: a new binding does not end the old one.
    let g0 = read_log(&ledger).len();
    {
        let _shadow = Trace::new("shadowed", &ledger);
        let _shadow = Trace::new("shadower", &ledger);
        println!("   at the moment of shadowing -> {}", new_entries(&ledger, g0));
    }
    println!("A shadowing                    -> {}", new_entries(&ledger, g0));
    tally.push(("shadowed", "scope end"));
    tally.push(("shadower", "scope end"));

    // --- B: where it was moved ---
    let b0 = read_log(&ledger).len();
    {
        let moved_in = Trace::new("into-function", &ledger);
        consume(moved_in);
        println!("   right after the function returns -> {}", new_entries(&ledger, b0));

        let inner = Trace::new("inner-scope", &ledger);
        {
            let _new_owner = inner;
        }
        println!("   as soon as the inner scope ends -> {}", new_entries(&ledger, b0));

        let field_value = Trace::new("into-struct", &ledger);
        {
            let container = Container { content: field_value };
            let _name_length = container.content.name.len();
        }
    }
    println!("B where it was moved            -> {}", new_entries(&ledger, b0));
    for name in ["into-function", "inner-scope", "into-struct"] {
        tally.push((name, "where it was moved"));
    }

    // --- C: an explicit call ---
    let c0 = read_log(&ledger).len();
    {
        let early = Trace::new("early-dropped", &ledger);
        let _late = Trace::new("stays-in-scope", &ledger);
        drop(early);
        println!("   right after the explicit call -> {}", new_entries(&ledger, c0));
    }
    println!("C an explicit call              -> {}", new_entries(&ledger, c0));
    tally.push(("early-dropped", "an explicit call"));
    tally.push(("stays-in-scope", "scope end"));

    // --- D: together with its container ---
    let d0 = read_log(&ledger).len();
    {
        let mut list = Vec::new();
        list.push(Trace::new("container-1", &ledger));
        list.push(Trace::new("container-2", &ledger));
        list.push(Trace::new("container-3", &ledger));
        let _boxed = Box::new(Trace::new("on-heap", &ledger));
        println!("   while the container is full -> [{}]", new_entries(&ledger, d0));
    }
    println!("D together with its container   -> {}", new_entries(&ledger, d0));
    for name in ["container-1", "container-2", "container-3", "on-heap"] {
        tally.push((name, "together with its container"));
    }

    // --- report ---
    let finished = read_log(&ledger);
    println!();
    println!("total finished values: {}", finished.len());

    let classes = ["scope end", "where it was moved", "an explicit call", "together with its container"];
    for class in classes {
        let count = tally.iter().filter(|(_, c)| *c == class).count();
        println!("{:<29}{}", class, count);
    }
}
```

```
A scope end                    -> scope-3, scope-2, scope-1
   at the moment of shadowing -> 
A shadowing                    -> shadower, shadowed
   right after the function returns -> into-function
   as soon as the inner scope ends -> into-function, inner-scope
B where it was moved            -> into-function, inner-scope, into-struct
   right after the explicit call -> early-dropped
C an explicit call              -> early-dropped, stays-in-scope
   while the container is full -> []
D together with its container   -> on-heap, container-1, container-2, container-3

total finished values: 14
scope end                    6
where it was moved           3
an explicit call             1
together with its container  4
```

#### Reading the Four Sections

Section A creates three values (`scope-1`, `scope-2`, `scope-3`), and all three drop
into the ledger the moment the inner scope closes — in the output, in the order
`scope-3, scope-2, scope-1`. This ordering is returned to in the Boundary Measurement
section. The second part of section A measures shadowing: binding the same name twice
(`shadowed`, then `shadower`) does not produce a single ending — the two live
separately, and both end at scope end. The measurement taken at the moment of shadowing
comes out empty, because nothing has ended yet; shadowing only sets up a new binding, it
does not shorten the old one's life. The Programming Fundamentals course established
shadowing as a concept, and the Variables and Immutability lesson deepened it with the
ability to change type; what this topic contributes here is narrow: shadowing is not an
ending.

Section B shows three separate forms of moving that fall into the same class: a value
being passed to a function (`into-function`), a value being rebound in an inner scope
(`inner-scope`), and a value being placed in a struct field (`into-struct`). All three
end before the outer scope closes, because ownership moves to a narrower scope each
time, and that narrower scope closes first. The `consume` function and the `Container`
struct were added to the core to make these three forms concrete: `consume` takes a
value as a parameter and drops it at the end of its scope without returning anything,
while `Container` places a value into its own field and shows that the owner of that
field is now the `Container` instance. Neither is part of the language — both are small
helpers written to place the three forms of moving side by side. The detail of these
three forms will be taken up in the Ownership and Moves lesson; here, only that all
three fall into the same class (where it was moved) is shown.

Section C shows a rare class with a single example: of two values in the same scope,
one (`early-dropped`) is dropped by a call before its scope ends, while the other
(`stays-in-scope`) waits for the normal end of scope. Only one of the fourteen values
ends this way — an explicit call moves an ending forward without waiting for the
scope's own flow.

Section D collects four values in a single container: a growable array holds three
elements (`container-1`, `container-2`, `container-3`), and a single heap-allocated
value (`on-heap`) sits in the same scope. While the container is full, the ledger comes
out empty — no element ends before the container closes. When the container closes, all
four values drop into the ledger at the same time, but they keep their own order among
themselves: `on-heap` ends before the list (the reverse of declaration), while the
list's own elements end in the order they were added.

**OR2.** The drop order of the elements inside a growable array — in insertion order,
from the lowest index to the highest — is the language's own guarantee; this behavior is
not an example the core chose, it is a rule the standard library defines.

#### Stack, Heap, and the Single Owner

Memory layout itself is not this course's subject — the How Computers Work course and
the Escape Analysis lesson had already established the stack/heap distinction as a
concept, and this third encounter is not a repeat of that theory. What this topic
contributes here is narrow, and it can be summed up in two sentences: the distinction is
**visible in the type**, and every value has **a single owner**.

The `on-heap` variable makes this concrete. An ordinary `Trace` value (`scope-1`, for
example) sits directly where it is bound; `Box::new(Trace::new("on-heap", &ledger))`, in
contrast, places the value on the heap, and this placement is visible in the code — the
type itself is `Box<Trace>`, not `Trace`. Heap allocation itself as a subject — how it
makes recursive types possible, how ownership behaves there — will be taken up later in
the Heap-Allocated Single Ownership lesson. The only thing that needs to be seen here is
this: `on-heap` drops into the same ledger through the same `Drop` mechanism, just like
the three array elements on the stack. Where a value lives (on the stack or on the heap)
and when it ends are separate questions — one is read from the type, the other from the
scope.

The phrase "single owner" is not a slogan here; it is an observation read straight from
the ledger itself. None of the fourteen values drops into the ledger twice — every name
appears exactly once across the whole output. Even when whether a value sits on the
stack or the heap changes, this singularity does not: `on-heap`, `container-1`, and
`scope-1` each end exactly once. This observation is not incidental; if a value could be
owned in two separate places at the same time, that value would be dropped twice, and
the same name would appear twice in the ledger. The next lesson measures directly how
this singularity is preserved during a move — why the old owner can no longer reach the
value once ownership changes hands.

#### Boundary Measurement: Drop Order Is the Reverse of Declaration

Section A's three values are the clearest example of this. `scope-1`, `scope-2`,
`scope-3` are declared in that order, but when the inner scope closes, the ending order
is exactly reversed: `scope-3, scope-2, scope-1`. This is not a choice made by the
measurement tool — it is the language's own word.

**OR3.** Values in a local scope always end in the reverse of their declaration order —
this is part of Rust's language specification and does not vary from implementation to
implementation. The same rule shows up in section D as well: `on-heap` ends before
`list`, which is declared after it.

This order is not incidental: the last binding opened in a scope ending first
guarantees that nested resources — a connection a file opened, for example — always
close in the reverse of the order they were opened. Someone reading the source can
state a scope's ending order without running anything, just by reversing the
declaration order.

This guarantee is not just an ordering detail — it also establishes a dependency order.
If a value bound second in a scope shares a resource belonging to a value bound first —
for example, holding the same ledger in its own field — the second value has to end
before the first one; otherwise, by the time it ended, the resource it held would
already be closed. Reversing the declaration order does exactly this: whatever opened
last ends while its dependencies are still alive. In the `life_core.rs` core, this
dependency is the `ledger` itself — every `Trace` value holds its own `ledger` field,
and that field ends not after the `Trace` value itself, but exactly together with it.

#### Summary

- A value's life ends in one of four forms: scope end, where it was moved, an explicit
  call, together with its container; of the fourteen values, 6 ended at scope end, 3
  where they were moved, 1 at an explicit call, and 4 together with their container.
- Shadowing is not an ending; rebinding the same name does not shorten the old
  binding's life.
- Whether a value lives on the stack or the heap is read from the type (the difference
  between `Trace` and `Box<Trace>`); where it lives is a separate question from when it
  ends.
- Values that end at scope end always end in the reverse of their declaration order;
  this is the language's word, not a choice made by the measurement tool.

#### Next Step

This lesson split where a value's life ends into four classes and measured scope end's
own order. Of the remaining three classes, the most common — where it was moved — has
so far only been seen through three examples. The next lesson deepens that class: when
ownership passes from one place to another, exactly what happens to the ending point,
and why three separate forms of moving (into a function, into an inner scope, into a
struct field) always arrive at the same result.

### Ownership and Moves

Source: https://academia.sh/en/courses/ownership/ownership-and-moves

Moving a value into a function, into an inner scope, and into a struct field all fall into the same class (where it was moved), and in all three the value ends exactly once; that the old binding cannot be used after a move is a rule the compiler tests at compile time, while the new owner can use the value without any problem.

The previous lesson measured how a value ends at scope end and only touched on the
where-it-was-moved class through three examples. This lesson deepens that class: when
ownership passes from one place to another, exactly what happens to the ending point?
The question the Concurrency course asks — how a goroutine stops — looks similar but is
separate: there, a running unit stops; here, a value ends.

The Programming Fundamentals course established as a concept how a value passes to a
function — passing by value; that course explained this form of passing through Python,
and showed that a value passed into a function does not affect the name in the caller's
hands. What this lesson measures is that Rust loads a completely different meaning onto
the same syntax (writing a value as an argument to a function): a value passed to a
function now belongs to the function too, and the name in the caller's hands loses its
validity. This change of ownership is tested at compile time, not at runtime. Accessing
a value through a reference without giving up ownership is a separate path — that path
is taken up in the Borrowing lesson.

#### Three Forms of Moving

The same class (where it was moved) can be entered through three separate paths. A
value can be passed as an argument to a **function**; a value can be rebound to a new
name in an **inner scope** opened inside the same scope; a value can be placed into a
**struct field**. What all three have in common is that the old binding no longer holds
the value — the value stays the same, only which name reaches it changes.

**OR4.** Binding a value to a new name in an inner scope (`let new_owner = inner;`)
should not be confused with the shadowing from the previous lesson. Shadowing created a
**new value** under the same name, and the old one went on living separately until its
own scope end. Here the exact opposite happens: **the same value** moves to a different
name, and the old name can no longer reach that value. Both look like a kind of
"rebinding," but one starts a new life, while the other changes the owner of a life
that already exists.

Although the three forms appear with different syntax, they are three use sites of one
single mechanism: in Rust, writing a value of a type not marked `Copy` anywhere — into a
parameter, a variable, a field — **moves** that value from there to there. The syntax
always looks the same (an `=` sign, a call), but the meaning behind it is the same every
time: the bit pattern at the old location moves to the new location, and the old
location no longer counts as valid. This lesson measures that all three separate places
(a parameter, an inner-scope binding, a struct field) reduce to this one single
mechanism — not three different rules, but three faces of one rule.

In practice, the three forms also correspond to different design decisions. Moving a
value into a function is preferred when the function is meant to have the final word
over that value — once the function ends, the value ends too, and the caller has no
more business with it. Rebinding a value in an inner scope shows up when the same value
needs to be kept under a different name, usually for a narrower, temporary operation.
Placing a value into a struct field, on the other hand, permanently makes the value part
of a larger whole — from then on, the value's life is tied not to its own scope but to
that struct's life. All three use the same mechanism, but the choice depends on which
unit (function, scope, struct) will draw the value's new boundary.

This choice mostly falls out of the data model itself. A program processing a record
that moves it into a function that validates it (moving into a function) expects the
record to stay there once validation ends — the caller side will not use the record
again anyway. If the same program wants to hold a record temporarily in a transformed
form (moving into an inner scope), the transformation stays valid only in that narrow
scope. If a record is going to be part of a persistent structure (a list or a cache, for
example — moving into a struct field), ownership is handed to that structure from the
start. The choice among the three forms is determined, while writing code, by the
answer given to the question "who does this value belong to now."

#### The Measurement Excerpt

Only the `Trace` type, the `Drop` implementation, and the observation-ledger excerpt are
taken from the previous lesson's core; sections A, C, and D are not needed in this
lesson. The helper part is placed in a separate file so that the three forms of moving
can be seen side by side in a single flow.

The helper file does not compile on its own — `trace.rs` has no entry point, so it
cannot be run; it is only written to the working directory. The `mod trace;` line at the
very top of `moves.rs` pulls this file into the main compilation; `use trace::{Trace,
new_ledger, read_log};` then states which names from it will be used directly. The
`Trace` struct's `name` field and the `new`/`new_ledger`/`read_log` functions are marked
`pub` so they can be reached from outside the module; the `ledger` field is not marked,
because only `Trace`'s own methods reach it. Marking visibility this way, item by item,
was already established in the Setup and the Package Manager lesson; here it is only one
application of it.

```rust
// trace.rs — a measurement helper that records a value's ending
use std::cell::RefCell;
use std::rc::Rc;

pub type Ledger = Rc<RefCell<Vec<String>>>;

pub struct Trace {
    pub name: String,
    ledger: Ledger,
}

impl Trace {
    pub fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

pub fn new_ledger() -> Ledger {
    Rc::new(RefCell::new(Vec::new()))
}

pub fn read_log(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}
```

```rust
// moves.rs — three forms of moving: into a function, into an inner scope, into a struct field
mod trace;
use trace::{Trace, new_ledger, read_log};

struct Container {
    content: Trace,
}

fn consume(received: Trace) {
    println!("name read inside the function: {}", received.name);
}

fn main() {
    let ledger = new_ledger();

    let moved_in = Trace::new("into-function", &ledger);
    consume(moved_in);
    println!("ledger after the function returns: [{}]", read_log(&ledger).join(", "));

    let inner = Trace::new("inner-scope", &ledger);
    {
        let new_owner = inner;
        println!("name read in the inner scope: {}", new_owner.name);
    }
    println!("ledger after the inner scope ends: [{}]", read_log(&ledger).join(", "));

    let field_value = Trace::new("into-struct", &ledger);
    {
        let container = Container { content: field_value };
        println!("name read in the struct field: {}", container.content.name);
    }
    println!("ledger after the struct scope ends: [{}]", read_log(&ledger).join(", "));

    let records = read_log(&ledger);
    println!();
    println!("total records: {}", records.len());
    for name in ["into-function", "inner-scope", "into-struct"] {
        let count = records.iter().filter(|k| k.as_str() == name).count();
        println!("{:<16}{} record", name, count);
    }
}
```

```
name read inside the function: into-function
ledger after the function returns: [into-function]
name read in the inner scope: inner-scope
ledger after the inner scope ends: [into-function, inner-scope]
name read in the struct field: into-struct
ledger after the struct scope ends: [into-function, inner-scope, into-struct]

total records: 3
into-function   1 record
inner-scope     1 record
into-struct     1 record
```

All three lines follow the same pattern: the value is first **read** in its new owner
(`received.name`, `new_owner.name`, `container.content.name`), then drops into the
ledger once the new owner's own scope closes. The moment `consume`'s body ends,
`into-function` is written to the ledger right away — the function's own scope becomes
the moved value's new and final scope. The same holds for `new_owner` in the inner
scope and `container.content` in the struct field: both end the moment their own narrow
scope closes, without waiting for the outer scope to close.

The manner of access also differs across the three forms. The value moved into a
function is reached directly by the parameter name (`received`), the value moved into
an inner scope by the new binding's name (`new_owner`), but the value moved into a
struct field no longer has a name of its own — it can only be reached through
`container.content`, through the struct that owns it. This third form shows that a move
is not just "copying one variable into another": once a value becomes **part of** a
struct, it loses its own independent name and becomes visible only through the field
path of the struct enclosing it.

#### Why the Old Binding No Longer Works

The code never reads the old name (`moved_in`, `inner`, `field_value`) again after these
three moves — because it cannot. If a line trying to reach `moved_in.name` were added
after the `consume(moved_in)` call, the compiler would reject it: ownership passed to
`consume`'s parameter, and `moved_in` no longer points to a valid value. The same rule
applies to `inner` and `field_value`.

**OR5.** This rejection is not a runtime error, it is a rule tested at compile time.
Source code trying to read a moved name again does not compile at all; the program
never reaches that line, so the wrong usage costs nothing at runtime. This also explains
why the accepted code above (the three blocks) never reads the old name again anywhere —
correct code already stays inside this constraint.

This rejection comes from the same place as the previous lesson's single-owner
observation: two separate names being able to reach one value at the same time would
make it unclear which name's scope closing would end that value. The compiler prevents
this by treating the old name as **invalid** the moment the move happens — the old name
still sits in the source, but it can no longer be used.

A single thought experiment is enough to make concrete what this constraint prevents: if
`moved_in` stayed valid after the move, both `moved_in` and `received` could reach the
same `Trace` instance. Both would try to end that instance once their own scope closed,
and the same value would end up being dropped twice — the same name would be written to
the `ledger` twice. The compiler never lets this situation arise; at the moment of the
move, it makes sure only one name stays valid, and leaves the rest (when the ending
happens) to the previous lesson's rules.

#### Moves Can Be Chained

The three forms each showed a single move, but moving can also happen one after
another: a value can enter a function and be handed from there to another function,
even back to the caller. The example below measures the same `Trace` value crossing two
separate function boundaries and ending in a third scope.

```rust
// chain.rs — a move can be chained: ownership can be handed from one function to another
mod trace;
use trace::{Trace, new_ledger, read_log};

fn second_stage(value: Trace) -> Trace {
    println!("name read in the second stage: {}", value.name);
    value
}

fn first_stage(value: Trace) {
    let handed_off = second_stage(value);
    println!("name back in the first stage: {}", handed_off.name);
}

fn main() {
    let ledger = new_ledger();
    let value = Trace::new("chained", &ledger);
    first_stage(value);
    println!("ledger after the chain ends: [{}]", read_log(&ledger).join(", "));
}
```

```
name read in the second stage: chained
name back in the first stage: chained
ledger after the chain ends: [chained]
```

The value here passes through the hands of four separate names: `value` in `main`,
`first_stage`'s parameter, `second_stage`'s parameter, and `handed_off`, which holds the
value `second_stage` returns. Each pass is a move, and after each move the previous name
is no longer valid. At the end of the chain the value belongs to `handed_off`, and it
ends there once `first_stage`'s body ends — not in `main`. Four names, one value, one
ending: only `chained` appears in the ledger, regardless of how many times it was moved.

This shows why the where-it-was-moved class is defined not by a count but by a
**position**. How many times the value changed hands does not matter; what is measured
is only the final owner's scope. `second_stage` returning the value is a move too — the
return value moves into a new binding on the caller's side, which is why `handed_off`
is itself the result of a move that falls to its own share.

A function returning a value it owns is a pattern this course will meet again. When a
function wants to hand back to its caller a value it built inside itself or received as
a parameter, the option is not a reference but an **ownership transfer** — the value is
written as the function's return type, and the return counts as a move in its own
right. This pattern is also needed to understand, in the Borrow Checker lesson, why a
function returning a reference to a value it built inside itself gets rejected: what
gets rejected is exactly this — returning a reference; what gets accepted is what is
seen here — returning the value itself.

#### Boundary Measurement: A Move Is Not a Copy

The output's last three lines show this directly: `into-function`, `inner-scope`, and
`into-struct` each appear in the ledger exactly **once**. Even if a value moved through
three separate names in sequence (it does not here, but suppose it did), each move
would not create a copy — the same `Trace` instance, the same memory location, only the
access name changes.

**OR6.** All three forms of moving produced exactly one record in the ledger; this
shows that the same value being dropped twice is impossible at the source level.
Because ownership always sits in a single name, the ending point is always triggered in
a single place too — a move does not break this singularity, it only changes which name
is responsible.

That a move does not **shorten** a life is also read from the same measurement.
`into-function` is the same `Trace` instance both before and after being moved; a move
is not a re-creation, it is a re-naming. This is why "where it was moved," out of the
four classes seen in the previous lesson, is a class in its own right: the value
neither waits for scope end nor receives an explicit call, it only inherits its owner's
own ending.

Thinking through the opposite case makes the difference clear. If `Trace` values
implemented `Clone` and the three lines had been written as
`consume(moved_in.clone())`, `let new_owner = inner.clone();`, the ledger would have six
records: the three original values ending in their own scopes, and three copies ending
separately in theirs. `clone` was never called in this lesson — a move does not build a
new `Trace` instance, it hands the existing one from hand to hand. The ledger's three
records, not six, are direct proof of this difference.

This difference shows up not only in the record count but also in the nature of the
work. A copy requires building the copy's own data separately — for `Trace`, allocating
a new `String`; a move, in contrast, amounts to no more than assigning a new owner to
existing data, with no new allocation at all. This is why the four changes of hands
seen in the chaining example all happened on a single `String` — not four separate
copies, but four separate owners of the same data.

#### Summary

- A value can be moved three ways — into a function, into an inner scope, into a
  struct field — and all three fall into the same class (where it was moved).
- A moved value can be used in its new owner; the old name, in contrast, can no
  longer be read after the move, because it no longer points to a valid value.
- The old name becoming unusable is a rule tested at compile time; it has no cost at
  runtime.
- A move is not a copy: the ledger gets exactly one record per value, and the same
  value is never dropped twice.
- A move can be chained — a value can cross several function boundaries one after
  another — but no matter how many times it changes hands, there is only one ending
  point at the end.

#### Next Step

What the three forms of moving had in common was that the old name lost the value —
ownership went exactly from one place to one place each time. The next question
investigates exactly the opposite: is it possible to reach a value without ever giving
up ownership? The Borrowing lesson measures that the name lending a value out does not
lose the value, and that a borrowed value never ends anywhere during the borrow —
unlike a move, here the old owner remains the owner.

### Borrowing

Source: https://academia.sh/en/courses/ownership/borrowing

A value can be read and written through a reference without ever giving up ownership: while the borrow lasts no record forms in the ledger, and once the borrow's scope closes the value is still accessible in its owner. A shared and a mutable reference exclude each other; a reference's scope always has to stay inside its source's scope.

The previous lesson measured how the ending point moves when ownership is transferred —
the moved value's old name lost its validity. This lesson measures how a value is
reached without ownership ever being transferred: borrowing. The question is this: does
the name lending a value out lose the value, and does a borrowed value end anywhere
while the borrow lasts? This is not to be confused with the Concurrency course's
question of how a goroutine stops: there, a running unit stops; here, a value ends.

The previous lesson's last section showed that a value can be moved into a function
one-way and irreversibly — or that even as the owner changed across a chain, the value
always ended up ending somewhere in the end. This lesson opens a different path for
values passed to the same kind of function: the same job can be done without ever
moving a value, just by giving it temporary access. Both paths are valid, but their
results can be read separately in the source.

The Programming Fundamentals course established as a concept how a value passes to a
function as a reference. What this lesson measures is that Rust splits this kind of
passing into two separate, mutually exclusive forms — a **shared** and a **mutable**
reference — and tests this distinction at compile time.

#### Borrowing Is Not Giving Ownership

A reference (`&T` or `&mut T`) is not the owner of the value it points to; it is only a
separate, small value that provides access to that value. A reference has its own scope
too, and the reference ends when that scope closes, but the reference ending does not
**affect** the value it points to — because the reference was never the owner of that
value to begin with, so it has nothing to trigger `Drop` when its scope closes.

Rust distinguishes two kinds of references. A **shared reference** (`&T`) can be taken
multiple times on the same value at the same time, and allows only reading. A **mutable
reference** (`&mut T`) can only be one at a time, and allows changing the value
alongside reading it. The two cannot exist together, at the same time, for the same
value.

These two kinds are not a choice, they are a requirement. Every piece of code that
wants to access a value — a function parameter, a local binding, a struct method — has
to state up front which kind it wants; even though the only difference in the syntax is
a single `mut` keyword, the authority it produces is entirely different. Because this
distinction is visible in the source, a reader can tell whether a reference can change
the value just by looking at the line where that reference was taken — without looking
at the value's own definition or how it is used.

**OR7.** A reference is a value in its own right and has its own scope, but a
reference to a type that implements `Drop`, like `Trace`, does not itself implement
`Drop`. A value of type `&Trace` or `&mut Trace` disappears silently at the end of its
scope; it writes nothing to the ledger, because the code that does the writing lives
only in `Trace`'s own `drop` method, and that method is only called when an owned
`Trace` ends.

This distinction sets up a direct contrast with the previous lesson. In the Ownership
and Moves lesson, a `Trace` value passed to a function was **moved** into the
function's parameter — once the function ended, the value ended too, and passing the
same value to a second call was not possible, because the old name stayed invalid after
the first call. If a function borrows a value instead, this constraint disappears: the
function uses the value and lets it go, ownership never changes hands, and the caller
side can reuse the same value as many times as it wants.

Every measurement in this lesson uses the same excerpt from the previous lesson's
core — `Trace`, the `Drop` implementation, and the observation ledger; the helper part
is placed in a separate file so later examples can pull it in again and again with
`mod`.

```rust
// trace.rs — a measurement helper that records a value's ending
use std::cell::RefCell;
use std::rc::Rc;

pub type Ledger = Rc<RefCell<Vec<String>>>;

pub struct Trace {
    pub name: String,
    ledger: Ledger,
}

impl Trace {
    pub fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

pub fn new_ledger() -> Ledger {
    Rc::new(RefCell::new(Vec::new()))
}

pub fn read_log(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}
```

```rust
// borrow_fn.rs — a function borrowing and returning a value, in contrast with a move
mod trace;
use trace::{Trace, new_ledger, read_log};

fn read(reference: &Trace) -> usize {
    reference.name.len()
}

fn main() {
    let ledger = new_ledger();
    let value = Trace::new("borrowable", &ledger);

    let length1 = read(&value);
    let length2 = read(&value);
    println!("first read: {}, second read: {}", length1, length2);
    println!("ledger after two calls: [{}]", read_log(&ledger).join(", "));
}
```

```
first read: 10, second read: 10
ledger after two calls: []
```

The `read` function borrows `value` twice, one after another. The Ownership and Moves
lesson's `consume` function could not have compiled with a call taking the same value a
second time, because the first call had already consumed ownership. Here both calls are
valid, because `read` never asks for ownership — its parameter type is `&Trace`, not
`Trace`. Even after two calls the ledger stays empty: `value` keeps living in its owner
all the way to the end of `main`.

The `&` sign in the `read(&value)` call is required here — the function asks for a
reference, and the source has to give it explicitly. The same requirement does not show
up elsewhere in the code (`a.name`, `value.name`): reaching a field with `.` or calling
a method is implicitly borrowed by the compiler when needed. The `reference.name.len()`
line works the same way — `reference` is already a `&Trace`, but the `.name` access and
the `.len()` call do not require writing a separate `&`. Implicit borrowing only makes
the source easier to read; its limits (shared or mutable, how long it lasts) are
subject to the same rules.

#### Measurement: The Ledger Stays Empty While the Borrow Lasts

The measurement below nests three levels of scope: `main` on the outside, `value`'s own
ownership scope inside it, and inside that, the shared and mutable borrow scopes in
turn. The helper file (`trace.rs`) is already in the working directory; this block
pulls it in again with `mod trace;`.

```rust
// borrow.rs — a borrowed value never ends anywhere while the borrow lasts
mod trace;
use trace::{Trace, new_ledger, read_log};

fn main() {
    let ledger = new_ledger();

    {
        let mut value = Trace::new("borrow-1", &ledger);

        {
            let a = &value;
            let b = &value;
            println!("shared read: {} and {}", a.name, b.name);
            println!("ledger while the borrow lasts: [{}]", read_log(&ledger).join(", "));
        }
        println!("ledger after the shared borrow ends: [{}]", read_log(&ledger).join(", "));

        {
            let m = &mut value;
            m.name.push_str("-mod");
            println!("name inside the mutable borrow: {}", m.name);
        }
        println!("ledger after the mutable borrow ends: [{}]", read_log(&ledger).join(", "));
        println!("value is still accessible, its own name: {}", value.name);
    }
    println!("ledger after the outer scope ends: [{}]", read_log(&ledger).join(", "));
}
```

```
shared read: borrow-1 and borrow-1
ledger while the borrow lasts: []
ledger after the shared borrow ends: []
name inside the mutable borrow: borrow-1-mod
ledger after the mutable borrow ends: []
value is still accessible, its own name: borrow-1-mod
ledger after the outer scope ends: [borrow-1-mod]
```

Four of the five separate measurement points show that the ledger is empty — while the
shared borrow lasts, after the shared borrow ends, while the mutable borrow lasts (this
does not show up separately since the line is printed inside it, but the next line
confirms it), and after the mutable borrow ends. `value` only ends when the outermost
scope (`main`'s own body) closes, and only then does the ledger get the entry
`borrow-1-mod`. That `a` and `b` can both exist at the same time — both only read
`value`, without interfering with each other — shows the plurality of a shared
reference.

The output's last line (`borrow-1-mod`) proves something beyond this: `value` itself
was never re-created. The change made through `m` was written to `value`'s own `name`
field; the name that drops into the ledger when `value` ends is not `borrow-1`, the name
at the moment it was created, but its form after being changed during the borrow. This
shows that borrowing is not a read-only view but a real access path — a shared
reference only reads, while a mutable reference can permanently change the source
itself.

#### Shared and Mutable References

The line `m.name.push_str("-mod")` really changes `value`'s own field through a mutable
reference — after this line, both `m.name` and, once the scope closes, `value.name`
carry the new value (`borrow-1-mod`). This was not possible with the shared references
(`a`, `b`): a line trying to change `a.name` would be rejected by the compiler, because
`&T` only grants permission to read.

**OR8.** Shared references being able to exist in multiple numbers at the same time
comes from the fact that none of them can change the value — reads do not interfere
with each other. A mutable reference being able to exist only one at a time, in
contrast, comes from the opposite reason: a change can lead to an unexpected result in
the middle of another read or another change. The rule looks not at the **number** of
references, but at whether a **change** exists among them.

The concrete counterpart of this distinction is that borrowing itself also carries two
separate authorities. The `read` function cannot change the value because it only asks
for `&Trace` — the parameter type itself prevents this, there is no need to even look
at the function's body. If a function wants to change a value, it has to write its
parameter as `&mut Trace`, and in that case that function cannot be called alongside
any other reference already borrowed on the same value at the same time. This means a
function's signature decides up front not only which type it takes, but also **what it
can do** with that type — just as the Rust's Design Goals lesson showed the class being
read from the signature, here the authority is read from the signature too.

#### Rejection: One Writer or Many Readers, Never Both

The code above used the shared borrow and the mutable borrow in separate scopes, one
after another — the two were never alive at the same time. This is not a coincidence,
it is required. If a line like `let m = &mut value;` had been added while `a` and `b`
were still in scope, the compiler would reject it: a shared and a mutable reference to
the same value cannot exist together at the same time. The same rule applies to two
mutable references — `let m1 = &mut value;` and `let m2 = &mut value;` cannot live
together in the same scope, because both carry write permission on the same field, and
it would become unreadable from the source which write is supposed to apply first and
which second. This is not a problem for the two shared references (`a` and `b`),
because neither writes — the order of reading does not change the result.

This rejection is not a runtime check; the source, the moment the two references'
scopes overlap, does not compile at all. The accepted code (above) already excludes
this overlap from the start: the shared scope closes with its own curly braces, the
mutable scope opens afterward with separate curly braces — the two scopes never overlap
at any point.

The same constraint applies to function calls too. `read(&value)` could be called twice
in a row because neither call's reference was still alive when the next call began —
each call opens its own borrow scope and closes it within itself. Code trying to pass
the same value to both `read` (`&Trace`) and a function that changes it at the same
time (`&mut Trace`) would not compile, because the two scopes overlap. The rule is not
limited to a single function's body; every reference scope in a source is subject to
the same check.

#### Boundary Measurement: Borrowing Does Not Extend a Life

The three levels of scope (`main`, `value`'s own scope, the borrow scopes) nest inside
each other, and this nesting is one-directional: a borrow scope always stays **inside**
its source's scope, never spilling outside it. `a` and `b` close their own scopes
before `value`'s scope closes; so does `m`. No reference lives longer than the value it
points to.

**OR9.** The interval during which a reference is valid can never exceed its source's
scope — this is the concrete counterpart of borrowing not **extending** a life. `value`
itself lives until `main`'s body ends; none of the references inside it can claim a
longer interval than that, because if it could, that reference would end up pointing to
a source value that had already ended.

This constraint was met here through nested scopes, with natural code: every borrow
opened and closed within its own curly braces, and `value`'s own scope enclosed all of
them. Code that tries to move a reference outside this nesting — to somewhere it would
still be valid after `value`'s scope, for example — is rejected by the compiler; this
interval's own name and rules will be taken up further ahead.

Why this constraint is required becomes clear when thinking through the opposite. If
the mutable reference named `m` could live longer than `value`'s scope, a read or a
write could be attempted through `m` even **after** `value` ended and was written to
the ledger — but by then there would no longer be a valid `Trace` left to point to. The
compiler prevents this scenario from the start by limiting the interval during which a
reference is valid to its source's scope. The `&Trace` parameter in the `read` function
is subject to the same constraint: no matter how much the function uses this reference
inside its own body, the reference's interval can never exceed the duration of the
`read` call — `value` on the caller's side is still in its owner, and the reference is
even re-taken between the two calls.

The three things measured in this lesson are connected to each other. Because
borrowing does not give ownership, a borrowed value never ends anywhere while the
borrow lasts; because a reference is itself a small value separate from the source,
with its own scope, it does not trigger the source's ending; and because a reference's
scope can never exceed the source's scope, the guarantee of "pointing to a value that is
still alive" is built in from the start. The shared/mutable distinction is a fourth
constraint layered on top of these three measurements: multiple reads at the same time
are safe, but a change can never happen at the same time as any other access. The
"single owner" principle from the Ownership and Moves lesson widens here into "one
writer, many readers" — ownership is still in a single name, but with that name's
permission, multiple temporary accesses can exist at the same time. This widening also
means the owner itself is never sidelined: no matter how many references `value` hands
out, it keeps being the single real owner within its own scope.

#### Summary

- A reference is not the owner of the value it points to; when its own scope closes,
  only it ends, without affecting the source.
- No record forms in the ledger while a borrow lasts; a borrowed value stays accessible
  in its owner even after the borrow's scope closes. The life of a value being lent out
  ends **at scope end**, not when the borrow ends.
- A shared reference (`&T`) can exist in multiple numbers at the same time and only
  reads; a mutable reference (`&mut T`) can exist only one at a time and can change the
  value.
- A shared and a mutable reference, or two mutable references, cannot exist together on
  the same value at the same time; this rule is tested at compile time.
- A reference's scope always has to stay inside its source's scope — borrowing does not
  extend a life.
- The same value can be passed to a borrowing function several times in a row; this is
  a direct contrast with a function that asks for ownership (like `consume`), which can
  only be called once. Reaching a field with `.` or calling a method also implicitly
  borrows when needed.

#### Next Step

This lesson measured references lent out over a value's **entirety** — `&Trace` or
`&mut Trace`, always the whole value. The next lesson applies the same
access-without-ownership principle to only a **part** of a value: how can we view a
specific range of a string or a growable array without ever ending the source? All the
rules of borrowing measured here (the source does not end, the life is not extended,
the shared/mutable distinction) will stay exactly the same; only the size of the viewed
part changes.

### Slices

Source: https://academia.sh/en/courses/ownership/slices

A slice views a specific range of a string or a growable array without taking ownership: the slice gets used, the slice ends, but the source never ends. A slice's length derives from the source, and an empty slice is valid; the source cannot be changed while the slice lives, and a change goes through without a problem once the slice ends.

The previous lesson measured references lent out over a value's **entirety**. This
lesson applies the same access-without-ownership principle to only a **part** of a
value: a slice, a kind of reference that views a contiguous range of a string or a
growable array without taking ownership. What is measured is again a value's ending,
not a running unit stopping — the Concurrency course's goroutine question stays outside
this course's axis.

The concept of a slice itself had already been established in the Concurrency
course — a view representing a contiguous sub-range of an array and carrying its own
length. What this lesson measures is that Rust builds a slice with a range syntax
(`[start..end]`), and that the previous three lessons' rules (ownership is not taken,
the source does not end, a borrow's life cannot exceed the source) apply here exactly
as they were.

The previous three lessons assumed a reference always pointed to **a single value**:
`&Trace`, `&mut Trace`. A slice loosens this assumption — a reference can now point to
several contiguous values at once, but in doing so it changes none of the rules of
ownership, scope, or the shared/mutable distinction. What is measured in this lesson is
that the rules stay the same; only what gets borrowed changes.

#### Viewing Part of a Value

A slice is a kind of `&T`: `&[Trace]` is a view taken from a growable array, `&str` is
a view taken from a string. Both point to the source's own data — they do not copy the
data — and separately carry their own length. When a slice is taken the source's owner
does not change; only temporary access to that range is set up.

The range syntax has the form `[start..end]`: `start` is included, `end` is excluded.
In its simplest form, with the ownership question never coming into play at all, the
range itself reads as follows.

```rust
// basic_slice.rs — slice syntax: the start..end range
fn main() {
    let numbers = [10, 20, 30, 40, 50];
    let middle = &numbers[1..4];
    println!("source: {:?}", numbers);
    println!("slice: {:?}", middle);
    println!("slice length: {}", middle.len());
}
```

```
source: [10, 20, 30, 40, 50]
slice: [20, 30, 40]
slice length: 3
```

`&numbers[1..4]` views three elements starting at index 1, up to (not including) index
4 — `20`, `30`, `40`. Because `numbers` carries a copyable type like `i32` here, the
ownership question does not show up; the real question arises when the source itself
carries a type that cannot be copied and implements `Drop` — exactly the situation the
rest of this lesson measures. The `[1..4]` syntax itself does not change `numbers`'s
type; `numbers` is still a fixed-length array, while `middle` is a separate type — a
slice with no length of its own, one that derives from the source.

#### Measurement: The Source Never Ends

The measurement below takes a slice viewing the first two elements from a growable
array holding three `Trace` values, uses the slice, closes the slice's scope, and then
changes the source.

```rust
// trace.rs — a measurement helper that records a value's ending
use std::cell::RefCell;
use std::rc::Rc;

pub type Ledger = Rc<RefCell<Vec<String>>>;

pub struct Trace {
    pub name: String,
    ledger: Ledger,
}

impl Trace {
    pub fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

pub fn new_ledger() -> Ledger {
    Rc::new(RefCell::new(Vec::new()))
}

pub fn read_log(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}
```

```rust
// slice_life.rs — a slice views without taking ownership; it does not end the source, but it locks it
mod trace;
use trace::{Trace, new_ledger, read_log};

fn main() {
    let ledger = new_ledger();
    let mut list = vec![
        Trace::new("one", &ledger),
        Trace::new("two", &ledger),
        Trace::new("three", &ledger),
    ];
    let before = read_log(&ledger).len();

    {
        let view = &list[0..2];
        println!("slice length: {}, first name: {}", view.len(), view[0].name);
        println!("ledger while the slice lives: [{}]", read_log(&ledger)[before..].join(", "));
    }
    println!("ledger after the slice ends: [{}]", read_log(&ledger)[before..].join(", "));

    list.push(Trace::new("four", &ledger));
    println!("list changed because the slice ended, length: {}", list.len());
}
```

```
slice length: 2, first name: one
ledger while the slice lives: []
ledger after the slice ends: []
list changed because the slice ended, length: 4
```

`view` only views `list`'s first two elements, but `list` is still the owner of all
three elements. The ledger stays empty both while the slice lives and after the
slice's scope closes — none of the values `one`, `two`, and `three` end, because the
slice never owned them, it only looked at them. `list` itself never ends in this
measurement (because the code does not close at the end of the file); the only ending
seen is the slice's own scope closing — and that writes nothing to the ledger either,
because a slice of type `&[Trace]` does not implement `Drop`.

One detail deserves attention here: `view` only views the first two elements (`one`,
`two`); the third element (`three`) is left outside the slice. Even so, the measurement
shows that `list` in its entirety still sits in its owner — `three`, which the slice
does not cover, and `one` and `two`, which it does, are unaffected in the same way. A
slice only opens a window to **read** a subset of the source; it does not interfere in
any way with the ownership or life of the rest of the source.

The `view[0].name` access makes this concrete too: `view[0]` produces a `&Trace`
pointing to the slice's first element (not ownership, a new borrow), and `.name` is
read through that borrow. Three separate borrows are nested in the same line — from
`list` to `view`, from `view` to `view[0]`, from `view[0]` to `.name` — but none of
them asks for ownership, all of them trace back to the same underlying `Trace` values.

**OR10.** A slice, just like an ordinary `&T` reference, is not the owner of the
elements it points to. When its scope closes, only it (the slice's own `start, length`
pair) disappears; none of the elements are affected by this. The previous lesson's rule
that "a reference does not end the source" applies here exactly as it was, only
visibility is over a contiguous range rather than a single value.

#### Boundary Measurement: A Slice Locks Its Source

The `list.push(...)` line runs **after** `view`'s scope closes. This ordering is
required: if the same line had been added while `view` was still in scope, the compiler
would reject it. `view` is a shared borrow of `list`, and the `push` call asks for a
mutable borrow to change `list` — the Borrowing lesson's "one writer or many readers"
rule applies here too, only what it borrows is no longer the whole value but a part of
it.

**OR11.** A slice locks its source's **entirety**, not just the range it views. Even
though `view` only views the first two elements, it also blocks adding an element
anywhere in `list` (at the end, for example) — because an insertion can rearrange a
growable array's own internal layout and invalidate the positions the slice points to.
The constraint is not limited to the elements the slice covers; it spreads across the
source as a whole.

This might look, at first glance, like it contradicts the statement that "a slice
views only part of the source" — the part viewed is small, but the surface locked is
large. There is no contradiction: a slice only grants permission to **read** the
elements in its own range, but because any change to the source's own layout (how many
elements it holds, where the elements sit) could break the assumption the slice relies
on, that change can never be started while the slice lives. What is viewed is narrow;
what is protected is broad.

The accepted code already preserves this order: the slice is used and let go within
its own narrow scope first, and `push` is only called afterward, at a point where
`list` has sole access. Trying to write the same job in reverse order — changing first,
then continuing to read the old slice — does not compile at all.

What this constraint prevents is concrete: `push` can reallocate a growable array's
backing storage when needed — as the array grows, all its elements can move to a new
location. If `view` pointed to the old locations and `push` triggered this move, `view`
would end up pointing to a region of memory that was no longer valid. The compiler
shuts this scenario down from the start by allowing no call that changes the source
while the slice lives — it does not even need to know whether `push` will actually
reallocate; the rule is the same for every mutating call.

The same constraint applies to reading a single element too: a direct index like
`list[2]` causes no trouble while `view` is in scope, because it also only reads — two
shared borrows can stand together. Trouble only starts when a **change** is requested
at the same time.

#### A Slice Passes to a Function Without Moving the Source

The Borrowing lesson's `read` function took a single value without asking for
ownership. The same mechanism applies to a slice too: if a function writes its
parameter as `&[Trace]`, it can accept both the source's entirety and just a range of
it, without asking for ownership.

```rust
// slice_param.rs — a function accepting a slice processes the source without moving it
mod trace;
use trace::{Trace, read_log, new_ledger};

fn join_names(slice: &[Trace]) -> String {
    let mut result = String::new();
    for (i, trace) in slice.iter().enumerate() {
        if i > 0 {
            result.push('+');
        }
        result.push_str(&trace.name);
    }
    result
}

fn main() {
    let ledger = new_ledger();
    let list = vec![
        Trace::new("a", &ledger),
        Trace::new("b", &ledger),
        Trace::new("c", &ledger),
    ];

    println!("full list: {}", join_names(&list));
    println!("first two: {}", join_names(&list[..2]));
    println!("ledger after the calls: [{}]", read_log(&ledger).join(", "));
}
```

```
full list: a+b+c
first two: a+b
ledger after the calls: []
```

The `join_names` function can be called with both `&list` (the entire source) and
`&list[..2]` (a sub-range) — because the parameter type is `&[Trace]`, both fit the same
function. `&list` is **converted** into a slice here: a reference to the growable
array's entirety automatically corresponds to a slice covering it start to end. The
ledger stays empty after both calls too — `join_names` never owned `list`'s elements,
it only read them. This is the concrete example of a function accepting **both the
whole and a part** with the same signature, without asking for ownership.

#### A Mutable Slice Changes the Elements It Covers

The slices seen so far have always been shared (`&[Trace]`). A slice can also be taken
as `&mut [Trace]`, and in that case it also carries write permission on the elements it
covers — the Borrowing lesson's shared/mutable distinction applies here exactly as it
was.

```rust
// mutable_slice.rs — a mutable slice can change the elements it covers
mod trace;
use trace::{Trace, new_ledger, read_log};

fn main() {
    let ledger = new_ledger();
    let mut list = vec![
        Trace::new("x", &ledger),
        Trace::new("y", &ledger),
        Trace::new("z", &ledger),
    ];

    {
        let view = &mut list[0..2];
        for trace in view.iter_mut() {
            trace.name.push_str("-new");
        }
    }
    println!("names after the change: {}, {}, {}", list[0].name, list[1].name, list[2].name);
    println!("nothing has ended, ledger: [{}]", read_log(&ledger).join(", "));
}
```

```
names after the change: x-new, y-new, z
nothing has ended, ledger: []
```

`view` here views `list`'s first two elements as mutable; `trace.name.push_str(...)`
inside the loop really changes these two elements' `name` field. The third element
(`z`) is left outside the slice, so it does not change — the output confirms this. No
value ends when the scope closes, because a mutable slice, just like a shared slice,
does not take ownership; it only carries write permission over that range.

In this example too there is only one mutable slice at a time; while `view` was in
scope, no other access to `list` (shared or mutable) could have been opened. Once
`view`'s scope closes, direct indexing like `list[0].name` can be freely used — just as
with the `push` example seen in the Boundary Measurement section, the constraint only
holds for the slice's own scope.

#### Length Comes From the Source, and an Empty Slice Is Valid

A slice's length does not carry information on its own — it is computed from the
source. The same range syntax applies on a string too.

```rust
// string_slice.rs — a slice's length derives from the source, and an empty slice is valid too
fn main() {
    let text = String::from("value");
    let full = &text[..];
    let empty = &text[0..0];
    println!("source length: {}", text.len());
    println!("full slice length: {}", full.len());
    println!("empty slice length: {}", empty.len());
    println!("is the empty slice valid: {}", empty.is_empty());
}
```

```
source length: 5
full slice length: 5
empty slice length: 0
is the empty slice valid: true
```

`&text[..]` views the entire range, while `&text[0..0]` views an empty range covering
no characters at all. `full.len()` comes out the same as the source's because the range
runs start to end; `empty.len()` is zero, but `empty` is not an invalid value — it is a
valid slice in its own right, it just covers nothing. The `is_empty()` call confirms
this: an ordinary method called on an empty slice runs without hitting any special
case.

The number `text.len()` gives here is the string's byte length — this, that a string is
itself a byte sequence and diverges from the character count, was already taken up in
the Collections lesson. What this lesson contributes is separate: how the range itself
is built and when it counts as valid. Because `text`'s content carries only ASCII
characters, the byte length coincides with the character count here; the difference
between the two is not this lesson's subject.

**OR12.** A slice having `start == end` is not an error condition, it produces a valid
slice of zero length. This lets a function return an empty range as, for example, the
"matching part," and lets the caller handle it without having to treat it as a separate
special case. `len()`, `is_empty()`, even iterating with a loop (running zero times)
work without a problem on an empty slice — even when the source itself is not empty.

The three questions borrowing has carried across four lessons give the same answers
here too: does the source end (no), is the life extended (no, a slice's scope can never
exceed the source's), and does the shared/mutable distinction apply (yes — a
`&mut [Trace]` slice could also have been taken, and its constraints would have been
identical to the mutable reference in the Borrowing lesson). A slice brings no new
answer to these three questions; the only new thing it has is the **size** of the
range it views — a contiguous set instead of a single value.

#### Summary

- A slice (`&[T]`, `&str`) views a contiguous range of the source without taking
  ownership; the source itself never ends because of the slice — the source's life
  ends at its own **scope end**.
- A slice locks not just the range it points to, but the source's **entirety** — the
  source cannot be changed while the slice lives, and a change goes through without a
  problem once the slice ends.
- A slice's length derives from the source; a slice does not carry independent length
  information on its own.
- A slice with `start == end` is valid and carries zero length; an empty slice is not a
  special case.
- Because a slice is a reference too, all the rules of the previous two lessons (the
  shared/mutable distinction, a scope never exceeding the source) apply here as well.
- Writing a function parameter as `&[T]` or `&mut [T]` lets that function accept both
  the source's entirety and just a range of it, with the same signature, without
  taking ownership.

#### Next Step

Across these four lessons, borrowing's rules were measured one by one — the whole
value, a part, shared or mutable; each time the source did not end, the life did not
exceed the source, and the one-writer-or-many-readers constraint held. These rules
stayed the same across four separate lessons without ever conflicting, only the
surface they applied to changed. The next lesson turns to the mechanism that enforces
these rules itself: what exactly does the borrow checker test at compile time, and what
kind of choice does this check leave between different pieces of code that do the same
job?

### The Borrow Checker

Source: https://academia.sh/en/courses/ownership/the-borrow-checker

The borrow checker tests a value's ownership and borrowing rules at compile time; three rejection examples (a live borrow during a move, mutating while reading, returning a reference to a local owner) are always described in prose, with an accepted solution run alongside each. Two different solutions doing the same job can produce the same ledger record; code that follows the borrowing rules can still give a wrong result — the checker guarantees not correctness, only ownership discipline.

The previous four lessons established borrowing's rules one by one: access without
giving up ownership, the source not ending, the life not exceeding the source, the
shared/mutable distinction. This lesson looks at the mechanism that enforces these
rules itself: what exactly does the borrow checker test at compile time? What the
checker tracks is a value's life — not the question asked in the Concurrency course,
"how does a running unit stop."

All four lessons shared one assumption: every piece of code shown already followed the
rule, and stood on the accepted side. This lesson makes that line itself visible — it
shows what the rule separates by describing the rejected side in prose and running the
accepted side. That multiple accepted pieces of code doing the same job can exist is
also a separate finding of this lesson: the rule does not impose a single correct
form, it only draws a boundary.

A compiler rejecting a piece of code was, for the first time in this curriculum, the
main measurement axis in the Go's Design Goals lesson — there, whether the source
compiled or not was itself the thing measured. In this course, the same question
**cannot** be the course's main measurement; it is only this lesson's limited
contribution. This lesson sees more rejection than any of the other four lessons, but
each one stays bound to the same rule: **a rejection is described in prose, an accepted
solution is run.**

The rejections in the previous three lessons were, one by one, concrete examples of a
single rule — in this lesson, three rejections stand side by side to show the checker's
general way of working. What all three have in common is this: the checker does not
read a source line by line, it tracks, as a whole, how long every value in the source
is valid and who accesses it with what authority during that time. A line looking valid
on its own is not enough; the relationship that line forms with the lines before and
after it also has to follow the rule.

#### Rejection 1: A Live Borrow During a Move

A value cannot be moved while it is lent out. A call trying to move `value` into a
function while a reference (`view`) to it is still held does not compile — because the
move invalidates the old name, but `view` would still be looking at where that name
pointed. No source showing this violation is written at all; instead, two separate
**accepted** solutions doing the same job are run, and both are measured to produce the
same ledger record.

This rejection is where the Ownership and Moves lesson's single-owner rule and the
Borrowing lesson's stay-inside-the-scope rule intersect. A move alone is free,
borrowing alone is free; the checker steps in when the two overlap on the same value,
at the same time. As the move invalidates the old name, what happens to a reference
left over becomes unclear — the checker resolves this by requiring every borrow on the
value to have already closed before allowing the move.

**Solution A — narrowing the scope:** the reference is left in its own narrow scope
and closed before the move.

```rust
// trace.rs — a measurement helper that records a value's ending
use std::cell::RefCell;
use std::rc::Rc;

pub type Ledger = Rc<RefCell<Vec<String>>>;

pub struct Trace {
    pub name: String,
    ledger: Ledger,
}

impl Trace {
    pub fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

pub fn new_ledger() -> Ledger {
    Rc::new(RefCell::new(Vec::new()))
}

pub fn read_log(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}
```

```rust
// narrow_scope.rs — the borrow is ended in a narrow scope before the move
mod trace;
use trace::{Trace, new_ledger, read_log};

fn process_task(value: Trace) {
    println!("name moved inside the function: {}", value.name);
}

fn main() {
    let ledger = new_ledger();
    let value = Trace::new("task", &ledger);

    {
        let view = &value;
        println!("current name: {}", view.name);
    }
    process_task(value);
    println!("ledger after narrowing the scope: [{}]", read_log(&ledger).join(", "));
}
```

```
current name: task
name moved inside the function: task
ledger after narrowing the scope: [task]
```

**Solution B — taking a copy:** the data needed for reading is copied up front, and no
reference to `value` is taken at all.

```rust
// take_copy.rs — the data to be read is copied beforehand, and the borrow is never taken
mod trace;
use trace::{Trace, new_ledger, read_log};

fn process_task(value: Trace) {
    println!("name moved inside the function: {}", value.name);
}

fn main() {
    let ledger = new_ledger();
    let value = Trace::new("task", &ledger);

    let current_name = value.name.clone();
    println!("current name: {}", current_name);
    process_task(value);
    println!("ledger after taking a copy: [{}]", read_log(&ledger).join(", "));
}
```

```
current name: task
name moved inside the function: task
ledger after taking a copy: [task]
```

**OR13.** Both solutions produce the same ledger record (`[task]`). The checker does
not impose a single correct piece of code; it only enforces one rule (there cannot be a
live borrow during a move), and every piece of code that satisfies this rule is
accepted. Narrowing the scope shortens the reference's life, taking a copy never sets
up the reference at all — both follow the same rule by a different path and arrive at
the same observable result.

The difference between the two solutions is in cost. Narrowing the scope builds no new
data at all, it only narrows the reference's scope; taking a copy, in contrast, produces
a copy of `value.name` — `Trace` itself is still one, but the content of the `name`
field gets built once more. Both pieces of code follow the same rule, but which one to
choose depends on the rest of the source: if the reference's scope can fit into a
narrow block, narrowing the scope is preferred; if the data to be read is needed
repeatedly at different points in the function, taking a copy can stay simpler.

#### Rejection 2: Mutating While Reading

Trying to change the same list while iterating over it with a shared borrow (a loop,
for example) is also rejected — while the loop itself holds a shared reference to the
list, adding an element to the list asks for a mutable reference, and the two collide.
The solution is to split reading and writing into two separate steps. This pattern is a
situation often met when new elements derived from each element of a collection are
meant to be added to the same collection — trying to grow a collection while iterating
over it.

```rust
// read_then_write.rs — first read with a shared borrow, then change in a separate step
mod trace;
use trace::{Trace, new_ledger, read_log};

fn main() {
    let ledger = new_ledger();
    let mut list = vec![Trace::new("p", &ledger), Trace::new("q", &ledger)];

    let mut copy_names = Vec::new();
    for trace in &list {
        copy_names.push(format!("{}-copy", trace.name));
    }

    for name in &copy_names {
        list.push(Trace::new(name, &ledger));
    }

    println!("list length: {}", list.len());
    println!("nothing has ended yet, ledger: [{}]", read_log(&ledger).join(", "));
}
```

```
list length: 4
nothing has ended yet, ledger: []
```

The first loop iterates over `&list` and collects a copy of each name into a separate
`Vec<String>`; when this loop ends, the shared borrow closes too. The second loop
changes `list` freely, because there is no borrow left on it anymore. Code trying to do
the same job in a single loop, adding an element to the list while iteration is still
going, would not compile.

This rejection is from the same family as Rejection 1, but its source is different. In
Rejection 1, the two things colliding were a move and a borrow; here, the two things
colliding are a shared borrow (the loop itself) and a mutable borrow (the `push` call).
What the two have in common is that the checker finds the collision not by looking at
the **order** of the lines, but by looking at whether the borrows' **scopes** overlap.
Even while the loop visits each of `list`'s elements one by one, the loop itself holds a
single, continuous shared borrow on `list` — it does not close until the loop ends.

#### Rejection 3: Returning a Reference to a Local Owner

A function cannot return a reference to a value it builds inside itself — once the
function ends, that value's owner (the function's own scope) ends too, and the
returned reference would end up pointing to somewhere that no longer existed. The
solution is the same pattern established in the Ownership and Moves lesson: returning
not a reference, but the value **itself**.

The first two rejections were a borrow colliding with a move, or a borrow colliding
with another borrow, in the same scope — in both, the two problematic accesses sat
between the same lines, in the same source. The third rejection is different: the
problem crosses a single function's boundary, between the caller side and the called
side.

```rust
// return_ownership.rs — the function returns the local value by ownership, not by reference
mod trace;
use trace::{Trace, new_ledger, read_log, Ledger};

fn create(name: &str, ledger: &Ledger) -> Trace {
    let local = Trace::new(name, ledger);
    local
}

fn main() {
    let ledger = new_ledger();
    let value = create("produced", &ledger);
    println!("produced name: {}", value.name);
    println!("value is still in its owner, ledger: [{}]", read_log(&ledger).join(", "));
}
```

```
produced name: produced
value is still in its owner, ledger: []
```

The `create` function builds `local` inside its own body and returns it **by moving
it** — not `&local`, but `local` itself. Because ownership passes to the caller, `value`
stays valid all the way to the end of `main`; the ledger stays empty. If `create`'s
signature had returned `&Trace`, the compiler would not have accepted even that
signature, because which source the returned reference was tied to would stay unclear —
`local`, built inside the function, ends once the function ends.

This rejection stands in a different place from the previous two: Rejection 1 and
Rejection 2 forbade a specific call order, the source itself was invalid from the
start. Here, what actually gets rejected is the function's **signature** — the code
`fn create(...) -> &Trace` is problematic without even looking at its body. Where a
reference a function returns is tied to is information that has to be known at every
place the function is called; when a function returns a reference to a value it builds
inside itself, this information is not written anywhere in the source. The rule seen in
the Ownership and Moves lesson — "a function returning a value it owns is a move" —
comes back here as the only solution.

**OR14.** This rejection is a direct consequence of the rule (from the Borrowing
lesson) that the interval during which a reference is valid can never exceed its
source's scope. A concept not yet named comes into play here: this interval, which
determines "how long" a reference can stay valid, has a name of its own, but this
lesson does not give that name.

Unlike the first two rejections, this one can be detected without looking at code
anywhere the function is called, just by reading `create`'s own body. The checker does
this by looking at the function's signature and tracking which parameter or which local
value the returned reference is tied to; because `local`'s source does not come from
outside the function, there is no way for a returned reference to stay valid outside
it.

#### Boundary Measurement: The Checker Does Not Guarantee Correctness

All the examples so far showed **what** the checker rejects. Now the opposite: code the
checker accepts, code that violates no rule at all, can still give a wrong result.

```rust
// correctness_not_guaranteed.rs — code that follows the borrowing rules can still give a wrong result
fn push_and_count(list: &mut Vec<i32>, value: i32) -> usize {
    let length = list.len();
    list.push(value);
    length
}

fn main() {
    let mut list = Vec::new();
    let count1 = push_and_count(&mut list, 10);
    let count2 = push_and_count(&mut list, 20);
    println!("counts returned by the function: {}, {}", count1, count2);
    println!("the list's real length: {}", list.len());
}
```

```
counts returned by the function: 0, 1
the list's real length: 2
```

`push_and_count` violates no ownership or borrowing rule at all — it takes `list` by a
mutable reference, adds the element, returns a valid number. But `length` holds the
length **before** the addition, and the returned value is wrong if it is meant to be
read as "the count after adding": the two calls return `0` and `1`, while the list's
real length should have been `1` and `2` respectively.

**OR15.** The borrow checker tests only **ownership and access discipline** — who
accesses a value at the same time, with what authority. At which point a computation is
read, whether a variable is updated on the correct line, are outside this check; a
source can carry a completely valid ownership/borrowing arrangement and still hold a
logic error. The one thing this lesson measures is this: a piece of code compiling does
not mean it is **correct**.

Fixing the error completes the same observation: the problem is not in ownership, it is
in which line `length` is read on.

```rust
// fixed.rs — same function, length is read after the addition
fn push_and_count(list: &mut Vec<i32>, value: i32) -> usize {
    list.push(value);
    list.len()
}

fn main() {
    let mut list = Vec::new();
    let count1 = push_and_count(&mut list, 10);
    let count2 = push_and_count(&mut list, 20);
    println!("counts returned by the function: {}, {}", count1, count2);
    println!("the list's real length: {}", list.len());
}
```

```
counts returned by the function: 1, 2
the list's real length: 2
```

`list.push(value)` and `list.len()` swapped places, nothing else changed — the
parameter type, the borrow, the signature all stayed the same. The fixed version passes
the borrow checker just like the buggy version, because the checker never sees the
difference between the two at all: in both versions, `list` is taken by a mutable
reference once, used once, and no rule is violated. The checker staying silent is not
proof the code is correct, only proof that the access rules were not violated.

Three rejections and one correctness boundary together draw the borrow checker's
limits. When the checker rejects a source, the reason is always from the same family:
more than one conflicting access existing on a value at the same time (Rejection 1,
Rejection 2), or a reference that would live longer than its source (Rejection 3). When
it accepts, what the checker says is narrow — only that this access arrangement is
safe, not that the computation the code performs is correct. The ownership, move,
borrow, and slice rules built across four lessons all tie into a single mechanism, a
single check, with this lesson; but the limits of what that mechanism tests are seen
just as clearly.

#### Summary

- The borrow checker tests, at compile time, whether the ownership and borrowing rules
  are violated; a source that violates them does not compile at all.
- There cannot be a live borrow during a move; narrowing the scope and taking a copy
  can both satisfy this constraint in two different ways, and both produce the same
  ledger record: the value ends at **scope end** in both pieces of code.
- Changing the same source while iterating over it with a shared borrow is rejected;
  it is solved by splitting reading and writing into separate steps.
- A function cannot return a reference to a value it builds inside itself; the
  solution is to return the value itself (by ownership), not a reference.
- Code that fully follows the borrowing rules can still give a wrong result — the
  checker guarantees not correctness, only access discipline.
- Multiple pieces of code following the same rule (narrowing the scope, taking a copy)
  can be accepted; the checker does not impose a single correct form, it only draws a
  boundary.

#### Next Step

This lesson repeatedly showed that what the checker tests is an **interval** — a
boundary that determines how long a reference can stay valid. In Rejection 1 this
interval had to avoid colliding with a move, in Rejection 2 it had to avoid colliding
with a change, and in Rejection 3 it could never exceed its source's own scope. All
three speak of the same thing, but this interval's own name was never given across
these five lessons. The next topic gives exactly this name: the concept of a
reference's own validity interval, when this interval can be read from the source, and
when it has to be written explicitly.

## Lifetimes

The establishment of a reference's validity range as a concept, the requirement to write an annotation when two sources' lives diverge, the rules by which the compiler derives an unwritten annotation, and the condition that holding a reference in a struct's field places on the struct's own life.

### The Lifetime Concept

Source: https://academia.sh/en/courses/ownership/lifetime-concept

Establishes that the source range over which a reference is valid is not a runtime object, only a range the compiler checks. The range shown to be checked in the Borrow Checker lesson is given a name here, and it is measured that two independent references' ranges need not be nested.

The Borrow Checker lesson showed that it was checking something, but it never named that
something: the checker was controlling a range tied to how long a reference could be used.
This lesson gives that range a name and applies the course's question ("where does this
value's life end") to a reference: where does a reference's **own** validity range start and
end, and where is that range read from?

The question asked by the Go curriculum's Concurrency course was different — there, what was
measured was when a running unit would stop, a stop triggered by an outside decision (a
channel closing, a context being canceled). What is measured here is not a unit of work; it
is the range in the source over which a reference can be used. The Borrowing lesson had
already shown this range's **consequence**: when a reference's scope ended, it did not end
anything itself — the value it borrowed was still alive. This lesson goes down to that
consequence's **cause** — whether the reference itself has a named range of its own, and if
so, how that range is determined.

#### A Range's Name

In Rust, a **lifetime** is the name of the source range over which a reference is considered
valid. This range has two ends: the point where the reference is created and the point where
it is last used. A lifetime defines the distance between these two points, and the compiler
makes sure a reference is never used outside that range. Without this check, a reference
could still be held after the value it pointed to had ended, and a read could be attempted
through it; the result would be an undefined read into memory that was no longer valid. Rust
never leaves this to runtime: a range violation is caught at compile time, before the program
has run even once.

There is one crucial distinction to establish here: a lifetime is not a **runtime** object.
Nowhere during the program's run is there a counter, a timestamp, or a record stored under
the name "this reference's lifetime." The `Trace` type measured in the previous lesson
implemented the `Drop` trait and wrote to the ledger when a value ended — but that was the
end of an **owned value**. A reference (`&Trace`) does not implement `Drop`; the reference
itself never drops into the ledger, because its "ending" is not an event that happens at
runtime but a boundary verified at compile time. A lifetime is **read** from the block
structure in the source; once the run is over, no trace of it remains.

**LI1.** A lifetime is not a value; it is the name of an inference the compiler carries out
over the source — it is stored nowhere while the program itself is running. It fits none of
the four end classes measured in the course's earlier lessons: a lifetime is not the end of a
value but the range over which a reference **is considered valid**; what is measured is not
an event but a source range.

Once established, this boundary will be used again and again through the rest of the course:
every new mechanism (explicit annotation, elision rule, struct field, heap-allocated single
ownership, shared ownership) will approach the same question from a different angle, but all
of them will rest on the same foundation — the fact that a reference cannot outlive its
owner.

When it needs to, Rust itself writes a lifetime down as a short name starting with an
apostrophe (`'a`, `'b`); this naming is not a value, only a way of telling the compiler and
the reader "this reference and that reference are tied to the same range." When these names
need to be written and what they express is the subject of the next lesson; the only thing
that matters here is that a range having a name is not what makes that range exist at
runtime.

#### The Difference Between Scope and Lifetime

The Programming Fundamentals course established the concept of scope: the block in which a
name appears, and the point past which it becomes unreachable once that block ends. A
lifetime is related to this concept but is not the same thing. Scope is a syntactic
boundary — it is drawn by a pair of `{` and `}` in the source and covers an entire block. A
lifetime, by contrast, is a **usage** range: the range over which a reference actually has to
be valid is not the whole block it sits in, only the segment from the point it is created to
the point it is last used.

The measurement below makes this distinction concrete: the references `r1`, `r2`, and `r3`
are created inside a scope but do not fill that scope **entirely** — after the line where
they are last used, several more lines of code still run before the scope closes, and those
lines never touch the references again. A reference's validity range runs from where it is
created to where it is last used; the rest of the scope still matters for the three values'
**ownership** (all three stay alive until the scope closes), but it no longer matters for the
references. This is why a lifetime is named as a separate concept: scope alone is not enough
to say how long a reference stays safe.

This difference cuts both ways: although a reference's range does not have to **fill the
scope entirely**, it also cannot **spill outside it**. A reference can never keep being used
after it has left the block it belongs to — a validity range is a subset of scope, not a line
independent of scope and fully detached from it. The explicit lifetime annotations seen in
later lessons do not break this relationship either; they only make a bond between several
references' ranges visible in the source when such a bond exists.

#### Measurement: A Reference's Range Stays Within Its Owner's Life

Part A of the measurement core in the Stack and Heap lesson had shown that three values
(`scope-1`, `scope-2`, `scope-3`) are set up in the same scope and end in the reverse order
of their declaration once the scope closes. The same slice is run again here, this time with
references taken: a reference is taken to each of the three values while still inside the
scope, a read is made through those references, and the ledger is checked at the moment of
that read.

```rust
// reference_range.rs — a slice of life.rs's part A: three values, read through references.

use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn read(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}

fn newly_added(ledger: &Ledger, before: usize) -> String {
    let all = read(ledger);
    all[before..].join(", ")
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));

    let a0 = read(&ledger).len();
    {
        let scope_1 = Trace::new("scope-1", &ledger);
        let scope_2 = Trace::new("scope-2", &ledger);
        let scope_3 = Trace::new("scope-3", &ledger);

        let r1 = &scope_1;
        let r2 = &scope_2;
        let r3 = &scope_3;
        println!("read through references -> {}, {}, {}", r1.name, r2.name, r3.name);
        println!("ledger at read time      -> [{}]", newly_added(&ledger, a0));

        // The references' last use was on the line above; none of them appear below, but
        // the three values keep living as owners until the scope itself closes.
        let still_empty = newly_added(&ledger, a0).is_empty();
        println!("ledger still empty while scope open -> {}", still_empty);
    }
    println!("once the scope closes   -> {}", newly_added(&ledger, a0));
}
```

```
read through references -> scope-1, scope-2, scope-3
ledger at read time      -> []
ledger still empty while scope open -> true
once the scope closes   -> scope-3, scope-2, scope-1
```

The references `r1`, `r2`, and `r3` are created and read at the same point, and the ledger is
still empty at that moment — all three values are still alive, because the scope has not yet
closed. The references' last use is on that very same line: they are never used again.
The line that follows touches none of the references and confirms the ledger is still empty —
all three values keep living as owners, even after the references have finished their work,
right up to the scope's own end. Once the scope closes they end in the reverse order of their
declaration — the rule established in the Stack and Heap lesson holds here too. The
references' own validity range sits **inside** the life of these three bindings; it does not
spill outside it, but it does not have to fill the entire life of the bindings either.

**LI2.** A reference's validity range is a sub-range of the life of the value it refers to —
a reference can never be used past the point where its owner ends. Code that crosses this
boundary is rejected by the compiler; the concrete form of that rejection was shown running
in the Borrow Checker lesson.

Suppose the measurement above tried to move a reference like `r1` outside the inner scope:
say `r1` were assigned to a variable defined in the outer scope, or returned from a function.
That code would not compile, because `scope_1`, the value `r1` is tied to, ends when the
inner scope closes, and `r1` would then be held somewhere that kept living past that point —
a reference outliving its own owner is exactly what a lifetime does not allow. The run above
was accepted because all three references stayed inside their own inner scope; the difference
between what is accepted and what is rejected is whether the reference's point of use falls
inside or outside its owner's scope.

#### Limiting Measurement: Ranges Need Not Be Nested

Textbooks often draw lifetimes as nested boxes, and that picture can leave a false
impression: as if every reference's range had to nest inside some outer range. In reality,
two independent references' ranges need not have any relationship to each other at all — they
do not even need to overlap.

```rust
// independent_ranges.rs — two references' validity ranges never intersect.

use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn read(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));

    let trace_a = Trace::new("independent-a", &ledger);
    let trace_b = Trace::new("independent-b", &ledger);

    {
        let r = &trace_a;
        println!("read through a -> {}", r.name);
    }
    println!("ledger after reading a -> [{}]", read(&ledger).join(", "));

    {
        let r = &trace_b;
        println!("read through b -> {}", r.name);
    }
    println!("ledger after reading b -> [{}]", read(&ledger).join(", "));
}
```

```
read through a -> independent-a
ledger after reading a -> []
read through b -> independent-b
ledger after reading b -> []
```

`trace_a` and `trace_b` live together in the same outer scope and both end only at the very
end of the program — the ledger stays empty after both reads, because neither has ended yet.
But the picture is different for the references: the reference taken to `trace_a` is created
in its own inner block and used there, and its validity ends once that block closes; the
reference taken to `trace_b` is created afterward, in a completely separate block. The two
references' validity ranges never overlap in time at all — one closes fully before the other
opens. The compiler never has to compare these two ranges at any point, because no
relationship was ever established between them.

**LI3.** Two references' validity ranges do not have to be nested or to intersect; ranges
that are fully independent of each other, even ones that never overlap in time, can sit side
by side in the same program. As long as a function signature does not establish a
relationship between two references, those two ranges stay independent for the compiler
too — when that relationship needs to be established is the subject of the next lesson.

The practical upshot of this independence is that dozens of references can be alive at once
in a program, and most of them have no relationship to each other at all — each exists in its
own independent range, tied to its own owner. The compiler only compares ranges that are
genuinely related: when one reference depends on another reference's data, or when a function
establishes a bond between two references. Two unrelated references never enter that
comparison, just as in the `trace_a` and `trace_b` example above. This also explains why the
explicit syntax seen in the next lesson only appears **when it is needed**: if there is no
relationship, there is nothing to write.

#### Reading the Range from the Source

The two measurements above share one feature: neither of them printed a lifetime directly.
The only thing printed was when the owned values ended — a reference's own range never
appeared in any output, because there is no runtime counterpart for it to appear as. The only
way to learn a reference's range is to read the source: `r1`'s range runs from the line
`let r1 = &scope_1;` to the print call where `r1.name` is used; there is no separate tool to
run either before or after that.

This continues the pattern established in the course's earlier lessons (the
expression/statement/item classification, the four end classes): every measurement produces a
conclusion drawn from the source's own structure, not a number or an ordered record left over
once the run is done. For a lifetime, this means information that never appears in the output
but is always visible in the source.

The same reading method holds in the second measurement: to find the range of the reference
on `trace_a`, there is no need to look at the ledger or at any other output — in the source,
the inner block from `let r = &trace_a;` to the line where `r.name` is used is the whole of
that range. The range on `trace_b` is bounded the same way, by its own inner block. The lines
that fall between the two references' ranges — after the first block closes, before the
second block opens — belong to neither range, and that too is understood only by reading the
source. No runner or extra tool contributes anything to this conclusion; the measurement core
exists here only to make concrete a reading that was already correct.

#### Summary

- A lifetime is the name of the source range over which a reference is considered valid, and
  it is not a runtime object — only an inference the compiler carries out over the source.
- A reference does not implement `Drop` and never drops into the ledger; only owned values
  do; an owned value's life ends **at scope end**.
- A reference's validity range is a sub-range of the life of the value it refers to, and this
  range cannot spill past its owner's end.
- Two independent references' validity ranges need not be nested or intersect.
- A reference's range never appears in any output; its only source is where in the source
  code the reference is created and last used.

#### Next Step

This lesson showed that a reference's range stays within its owner's life, that this range
leaves no trace at runtime, and that two independent ranges may enter no relationship with
each other at all. But some functions have to establish exactly that kind of relationship:
when a function is written that takes multiple references and returns one of them, which
input the returned reference is tied to cannot be inferred from the source alone, and the
compiler does not accept that ambiguity. The next lesson measures how the range is written
explicitly in that case, what information this writing adds to the source, and what happens
when what is written does not actually hold.

### Explicit Lifetime Annotations

Source: https://academia.sh/en/courses/ownership/explicit-lifetime-annotations

When a function takes multiple references and returns one of them, which input the returned reference is tied to cannot be inferred from the source alone, and an explicit lifetime annotation is required. It is measured that the annotation neither extends nor shortens a reference's life, only writes an existing relationship into the source, and that the compiler validates this writing as a request.

The previous lesson showed that two independent references' ranges can sit side by side
without entering any relationship — most references live unaware of each other, and the
compiler builds no bond between them. But when a function takes multiple references and
returns one of them, independence is no longer enough: which input the returned reference
comes from, which input's life it is tied to, cannot be inferred from the source alone. This
lesson measures the explicit syntax that steps in right at this point — the lifetime
annotation: when it is needed, what information it adds, and how the compiler treats it as a
promise.

What the Go curriculum's Concurrency course measured does not apply here either: there, a
running unit's stopping was triggered by an outside decision; what is measured here is a
relationship written into a source's own signature. None of the four end classes established
in the Stack and Heap lesson operate here either; the annotation does not write down the end
of a value, but a relationship between two references.

Writing this relationship is not optional, it is mandatory: the compiler rejects a signature
that stays ambiguous without making any assumption. A human reader looking at a two-reference
function might guess "probably the first parameter is returned," but the compiler does not
guess — either the relationship is written explicitly or the function does not compile at
all. This repeats a principle the course established earlier: the compiler's guarantee rests
on information written in the source, not on a probable correct reading.

#### When It Needs to Be Written

If a function takes a single reference and returns a reference, which input the returned
reference is tied to is obvious: the one input it took. But in a function that takes two
references and returns one, that bond becomes ambiguous — the compiler cannot tell from the
type in the source alone whether the returned reference is tied to the first input, the
second, or both. It is exactly at this moment of ambiguity that a lifetime annotation is
needed: a short name written into the function signature (such as `'a`) says which input's
life is carried into the returned reference.

This ambiguity is not only about the number of inputs, it is also about the returned value
being a reference. If a function takes two references and returns a number or a freshly built
value, no annotation is needed at all, because the returned value's life is not tied to any
input — it exists on its own. The annotation only comes into play when the returned value
**is also a reference** and which input that reference comes from cannot be read from the
type. The measurement below sets up exactly this situation: a function that takes two `Trace`
references but returns a string slice derived from only one of them.

```rust
// two_source_return.rs — the returned reference is tied only to the input carrying 'a.

use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn read(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}

// 'a is established only between primary and the returned value; other does not enter this bond.
fn primary_name<'a>(primary: &'a Trace, other: &Trace) -> &'a str {
    println!("other is visible too -> {}", other.name);
    &primary.name
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));

    let outside = Trace::new("lives-outside", &ledger);
    let result;
    {
        let inside = Trace::new("lives-inside", &ledger);
        result = primary_name(&outside, &inside);
        println!("result inside the call -> {}", result);
    }
    println!("ledger once inner scope closes -> [{}]", read(&ledger).join(", "));
    println!("result still usable -> {}", result);
}
```

```
other is visible too -> lives-inside
result inside the call -> lives-outside
ledger once inner scope closes -> [lives-inside]
result still usable -> lives-outside
```

`primary_name` takes two references, but the annotation is established only between
`primary` and the returned value: both carry the same `'a`, `other` does not. This is a
situation where the two sources' lives **diverge** — `outside` lives in the outer scope,
`inside` lives in the inner scope, and their lives are of different lengths. The measurement
confirms this: even after the inner scope closes and `lives-inside` drops into the ledger,
`result` is still usable, because `result` is tied only to `outside`'s life — not to
`other`'s (that is, `inside`'s) life at all.

**LI4.** An explicit lifetime annotation writes a relationship that **already exists**
between two references into the source; it does not create the relationship, it makes an
existing one visible. In `primary_name`, this relationship holds only between `primary` and
the returned value; `other` stays outside the annotation even though it is used in the
function's body.

In the measurement, `result` staying usable after `other` (tied to `inside`) has vanished is
not a coincidence; it is a consequence stated directly by the signature. `outside` was set up
before `inside` and is still standing until the inner scope closes; because `result` is also
tied to `outside`'s life, it stays valid even once `inside` ends. Had the function returned
`other` instead, that same call would have produced a reference that could no longer be used
once `inside`'s life ended — which is exactly why the signature chooses `primary`, not
`other`.

There is one more point worth noting here: writing `'a` neither extends nor shortens
`outside`'s life. `outside` was already in the outer scope, set up before `inside`, and would
have lived until the outer scope's end regardless — that would not have changed without the
annotation. The annotation's only job is to tell the compiler and the reader **which** life
the returned reference is tied to; it does not change any life itself.

#### What the Annotation Covers and Does Not Cover

In `primary_name`'s signature, `'a` appears only between `primary` and the returned value;
the `other` parameter has its own separate, unnamed life, and that life has no relationship
with `'a` at all. This shows that two references being present together in the **same**
function call does not mean a relationship exists between them — a relationship exists only
to the extent it is **written** in the signature. `other`'s life could be shorter than
`primary`'s or longer; the function does not care either way, because the body only uses
`other` to read it, not to return it.

This shows that it is a virtue for an annotation to say as little as possible in a signature.
We were not required to give the `other` parameter a name — if its own life is never used
anywhere, the compiler treats it, on its own, as a separate and unnamed range. A name is
needed only when a genuinely shared relationship exists; if there is no relationship, there
is no name either.

This distinction rests on the same root as the previous lesson's limiting measurement: as
long as a function signature does not establish a bond between two references, those two
references' ranges stay independent for the compiler. Here, `'a` tied only `primary` and the
returned value together; it left `other` outside that bond, and `other` stayed in its own
independent range.

A signature can also carry more than one named life instead of a single `'a` — each
establishes its own relationship. The function below takes two references and returns both of
them inside a tuple; `'a` ties only the first part, `'b` ties only the second, and no
relationship is established between the two.

```rust
// two_named_lifetimes.rs — two separately named lifetimes, each tied to its own part of the returned tuple.

use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn read(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}

// 'a and 'b are independent of each other: neither constrains the other.
fn return_both<'a, 'b>(x: &'a Trace, y: &'b Trace) -> (&'a str, &'b str) {
    (&x.name, &y.name)
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));

    let outside = Trace::new("long-lived", &ledger);
    let first;
    {
        let inside = Trace::new("short-lived", &ledger);
        let (a, b) = return_both(&outside, &inside);
        first = a;
        println!("inside the inner scope -> {}, {}", first, b);
    }
    println!("ledger once inner scope closes -> [{}]", read(&ledger).join(", "));
    println!("first still usable -> {}", first);
}
```

```
inside the inner scope -> long-lived, short-lived
ledger once inner scope closes -> [short-lived]
first still usable -> long-lived
```

The tuple's second part (`b`, tied to `'b`) is only used inside the inner scope and goes out
of relevance along with that scope — `inside` drops into the ledger. The tuple's first part
(`first`, tied to `'a`) is stored in the outer scope and stays usable even after `inside` has
ended. The two named lives here really did correspond to lives of different lengths, and the
compiler accepted this because the signature asked for no equality between the two. `'a` here
and the single `'a` in `primary_name`'s signature correspond to different concrete ranges in
different functions even though they carry the same letter — a name is a label valid within
its own signature's scope, not a record shared across functions.

#### Limiting Measurement: The Annotation Is Not a Promise, It Is a Request

Writing an annotation may look like giving the compiler information, but it is really a
**request**: a way of saying "let the returned reference live at least as long as `'a`." The
compiler has to verify this request, and if the request does not hold up against what the
body actually does, the code is rejected. This is what sets an annotation apart from a
documentation note: a wrong comment line does not affect compilation, but a wrong lifetime
annotation means compilation never happens at all.

If `primary_name`'s body had tried to return `other.name` — with the signature left unchanged
and only the last line changed — the compiler would not have accepted it. The signature ties
`'a` only to the `primary` parameter; `other`'s own life is not associated with `'a`, so as
far as the compiler is concerned, how long `other` will live after the call is unknown. Once
the returned value has been promised to live as long as `'a`, if the body returns a value
from a source (`other`) that does not satisfy that promise, the promise cannot be kept, and
the compiler rejects the signature; the reason for the rejection is that the `'a` annotation
was left out for `other` as well.

The compiler has no other option here: how long `primary` will live during the call is known
from the signature, but `other`'s is not. Unless the compiler can be sure the caller will
always supply, for `other`, a reference that lives at least as long as `'a`, it cannot let the
body return a value from `other` — otherwise, a caller could pass a short-lived `other` and
cause the returned reference to outlive its owner. This shows that the boundary established
in the previous lesson (a reference cannot outlive its owner) also holds across function
boundaries.

There are two ways to make this request satisfiable. The approach seen in the Ownership and
Moves lesson would work here too: if the function returned an owned value instead of a
reference (something like `other.name.clone()`), the lifetime problem would never arise,
because the returned value would no longer have a life tied to any input. This lesson does
not follow that path; it follows the second path, the one that keeps the reference: tying
both parameters to the same `'a`, so that both `primary` and `other` promise to live at least
as long as `'a`, and the body can return either one.

```rust
// unified_lifetime.rs — accepted once the request is made satisfiable: both parameters carry 'a.

use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

// Now both are 'a: the returned value can come from either parameter.
fn other_name<'a>(_primary: &'a Trace, other: &'a Trace) -> &'a str {
    &other.name
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    let a = Trace::new("a", &ledger);
    let b = Trace::new("b", &ledger);
    println!("{}", other_name(&a, &b));
}
```

```
b
```

`other_name` can now return `other.name`, because the signature now says both parameters
live at least as long as `'a`, and the body satisfies that promise — whichever parameter is
returned, the promise holds. The difference between `primary_name` and `other_name` is not
in behavior but in the **promise the signature covers**: the first promised only for one
parameter, the second promises for both.

**LI5.** A lifetime annotation is a request the compiler has to verify. If the body returns a
value from a source that does not keep the promise the annotation makes, the code is
rejected; once the promise is broadened to cover both sources, the same body is accepted.

Tying both parameters to the same `'a` in `other_name` is not free. When `primary_name` was
called, `other` living shorter than `primary` was not a problem at all, because the
annotation did not cover `other` in any way. When `other_name` is called, both now have to
live at least as long as `'a`; the caller cannot hand `other_name` a shorter-lived reference,
because the signature does not allow it. Broadening the promise does not make the function
more flexible — quite the opposite, it tightens the condition the caller has to meet. This
shows that an annotation determines not only "what information is added" but also "what the
added information places on the caller" — the narrowest annotation, only as much as the body
actually needs, is generally the one to prefer.

Together, these three measurements showed three faces of the annotation: `primary_name`
**wrote** a relationship, `return_both` showed that two relationships can stay
**independent**, and `other_name` showed that a relationship `primary_name` **could not
satisfy**, gets **accepted** once the annotation is broadened. In all three, the annotation
itself never changed any reference's life; it only put into writing a relationship that
already existed, or was wanted, in the source, and the compiler tested that writing for
consistency.

#### Summary

- An explicit lifetime annotation is needed when a function takes multiple references and
  returns one of them, and which input the returned reference is tied to cannot be inferred
  from the source.
- The annotation writes a relationship that **already exists** between references into the
  source; it neither extends nor shortens any reference's life — the values referenced still
  end at their own **scope end**.
- A signature can carry more than one named life; each name is valid only within its own
  signature's scope, and the same name in different functions is not tied to the same range.
- The annotation is a request the compiler has to verify: the returned value must come from
  the source the annotation promised.
- A body that does not satisfy the promise is rejected; once the promise is broadened to
  cover all relevant sources, the same body is accepted, but this broadening also tightens
  the condition placed on the caller.

#### Next Step

This lesson showed when an annotation **needs to be written**, what it expresses, and why it
is rejected when it is not satisfied. But most functions in the course's earlier
lessons — single-input functions, or methods that take `self` — carried no explicit
annotation at all and still compiled; in those functions, a relationship was already
established even though no annotation was written. The next lesson fills that gap: it
measures which rules the compiler uses to derive an unwritten annotation, how many signature
forms those rules cover, and which signatures fall outside them.

### Lifetime Elision Rules

Source: https://academia.sh/en/courses/ownership/lifetime-elision-rules

Measures the rules the compiler uses to derive a lifetime annotation that is not written in a signature. The same signature is written and run in both explicit and implicit form, and both are shown to give the same behavior; a rejection-acceptance pair tests a signature elision does not cover, where the annotation stays mandatory.

The previous lesson measured when explicit annotation is required and what it expresses. But
most of the functions seen in the course's earlier lessons — ones that take a single input
and return a reference, or methods that operate through `self` — carried no explicit
annotation at all and still compiled. This lesson fills that gap: which rules the compiler
uses to derive the unwritten annotation, and what happens in signatures those rules do not
cover.

This question resembles the axis of the Go Fundamentals course — "what fills in for what is
not written" — but it is not the same thing: what was measured there was where an unwritten
type's information was completed from by the compiler; what is measured here is **where** a
reference's life ends — elision rules only lift the requirement to write this information
into the source, they do not change the information itself. The question the Go curriculum's
Concurrency course asked — "how does a running unit stop" — does not apply here either; this
lesson still measures a reference's validity range, not a unit of work stopping.

Elision itself is not a new source of information. The principle established in the previous
lesson still holds: the compiler never guesses, it only shortens the writing in cases where it
would **always reach the same conclusion**. When a signature form falls within elision's
scope, that means every signature of that form has exactly one correct reading; the path from
input to output carries no more than one possibility. This lesson measures where that scope
begins and where it ends.

This also has an important consequence for someone reading the source: if no annotation
appears in a signature, that can mean two different things. Either the function has no
reference relationship with the returned value at all, or the relationship exists but the
signature form falls into one of the two patterns elision covers. The difference between
these two is told apart by looking at the signature and checking the number of inputs and
whether `self` is present — the two measurements below make this check concrete.

#### Three Rules, Two Conveniences

The relationship the compiler assumes when an annotation is not written in a signature is
called **elision**. Rust's language specification defines this assumption with three rules.
The first rule gives every input reference its own separate, unnamed life — this always holds
and produces no output on its own. The second rule, if the input has exactly one reference,
assigns that input's life to every elided output reference. The third rule, if `&self` or
`&mut self` is among the inputs, assigns `self`'s life to every elided output reference.

The second and third rules are, in practice, the ones that make an annotation unnecessary —
the first rule only names the input side, it ties the output to no input at all. For example,
in a function that takes two references and returns no reference at all, only the first rule
applies: each input gets its own separate life and the matter ends there, because there is no
output to elide. The second and third rules only come into play when there is an elided
output reference, and they determine which input that output is tied to.

The second rule's "exactly one input" condition looks at the **number of references** among
the inputs, not the number of inputs. If a function takes a number together with a reference,
even though the input list carries two items, only one input carries a life — the second rule
still applies. In the same way, if a function takes a single reference and returns a tuple
containing multiple references, every elided output part gets the life of that same single
input; the rule applies not to "a single output" but to "every elided output." The two
measurements below show these two conveniences separately: first a single-input function,
then a method that takes `&self`.

#### Measurement: A Single-Input Signature

If a function takes only a single reference and returns a reference, the second rule comes
into play: the life of the single elided input is assigned to the elided output. Below, the
same function is written twice — once without an annotation, once with an explicit `'a` — and
both give the same result.

```rust
// elision_single_input.rs — single-input signature: same behavior even without the annotation.

struct Trace {
    name: String,
}

// Implicit: the compiler assigns the single input's life to the returned reference.
fn first_three_letters(trace: &Trace) -> &str {
    &trace.name[..3]
}

// Explicit: the same relationship written out by hand.
fn first_three_letters_explicit<'a>(trace: &'a Trace) -> &'a str {
    &trace.name[..3]
}

fn main() {
    let value = Trace { name: "scope-1".to_string() };
    println!("implicit -> {}", first_three_letters(&value));
    println!("explicit -> {}", first_three_letters_explicit(&value));
}
```

```
implicit -> sco
explicit -> sco
```

`first_three_letters` and `first_three_letters_explicit` carry the same body and give the
same output; the only difference between them is whether the annotation is visible in the
source. The compiler builds the same relationship for the first as it does for the second
while compiling it — and there is no other possibility, because the input is single: the
returned reference either comes from `trace` or does not exist at all.

**LI6.** Elision rules make an annotation unnecessary in cases where the compiler would
**always** reach the same conclusion. In a single-input signature there is no real ambiguity
about which input the returned reference will be tied to — there is already only one input.

This is seen more clearly next to the previous lesson's `primary_name` function:
`primary_name` could not be written without an annotation because it took two inputs, since
whether the returned reference came from `primary` or from `other` could not be inferred from
the source. `first_three_letters` takes a single input, so that same ambiguity never
arises — the elision rule makes no real choice here, it only spares the source from writing
down an option that was already the only one.

#### Measurement: A Method That Takes `self`

The third rule ties the elided output to `self`'s life at all times, even if a method takes
`self` along with other references. This explains why many of the methods seen in the
course's earlier lessons carried no annotation at all.

```rust
// elision_self.rs — a method taking &self: the returned reference ties to self even without the annotation.

struct Trace {
    name: String,
}

impl Trace {
    // Implicit: self's life is assigned to the returned reference.
    fn name(&self) -> &str {
        &self.name
    }
}

struct TraceExplicit {
    name: String,
}

impl TraceExplicit {
    // Explicit: the same assignment written out by hand.
    fn name<'a>(&'a self) -> &'a str {
        &self.name
    }
}

fn main() {
    let value = Trace { name: "scope-2".to_string() };
    let explicit = TraceExplicit { name: "scope-2".to_string() };
    println!("implicit -> {}", value.name());
    println!("explicit -> {}", explicit.name());
}
```

```
implicit -> scope-2
explicit -> scope-2
```

`Trace::name` and `TraceExplicit::name` give the same behavior. What stands out here is that
the result would not change even if the method took another reference besides `self` — the
third rule looks only at `self`, it does not take the other inputs into account at all. This
is the same conclusion resting on a different foundation than the second rule: the compiler
assumes that in a method taking `self`, the returned reference is almost always part of
`self`, and this assumption was considered reliable enough to be turned into a rule.

Together, these two rules also explain why most of the methods seen in the course's earlier
lessons carried no annotation: methods that returned a reference to a type's own field took
`&self`, and the third rule came into play; free functions that read a value and returned a
derived reference were mostly single-input, and the second rule came into play. Where an
explicit annotation genuinely appeared, as in the previous lesson, was in signatures where
multiple inputs were present together without `self`.

**LI7.** Two different rules produce the same outcome in two different signature forms: in
single-input functions the input itself determines the output's life; in methods taking
`self`, `self` itself determines it. In both forms the signature is accepted even when no
annotation is ever written; what the two have in common is that there is only one place the
returned reference can go.

#### The Order for Reading a Signature

Together, the two measurements above also give an order for determining, by looking at a
signature, whether an annotation is required. First, the number of references among the
inputs is counted: if there are no references, or no output reference to elide, the first
rule alone is enough and the matter is settled. If the input has exactly one reference, the
second rule comes into play and the output is tied to it — `first_three_letters` takes this
path. If the input has more than one reference, the next question is whether `self` is
present: if it is, the third rule comes into play and the output is tied to `self` —
`Trace::name` takes this path. If there is more than one reference and `self` is absent, none
of the three rules can reach a single conclusion, and an explicit annotation becomes
mandatory — `longer_name` falls into this last case.

This ordering is less about the compiler's own inner workings and more a useful shortcut for
a reader to quickly judge, by looking at a signature, whether to expect an annotation. Most of
the functions seen through the course are settled in the first two steps; the places where an
explicit annotation appears are exactly the signatures that fall into this ordering's last
step, the ones carrying more than one reference of equal weight.

This ordering itself requires no tool or checker — the three items can be memorized and
applied by hand to any signature. This repeats the same pattern as the earlier measurements
about lifetimes: the source, on its own, without being run, carries information that can be
read. The difference is that what is read here is not a reference's range but the decision of
whether that range is **written down** or not.

#### Limiting Measurement: Elision Is a Convenience, Not a Rule

Elision rules are limited to three items, and there are signatures those three items do not
cover. The clearest example is a function that contains no `self` and takes more than one
reference: neither the second rule (no single input) nor the third rule (no `self`) comes into
play. In such a function there is no single conclusion the compiler can reach on its own about
which input the returned reference is tied to — the first rule gives every input its own
separate life and assigns none of them to the output.

The two-reference, `self`-free function seen in the previous lesson (`primary_name`) was in
exactly this situation and carried an explicit `'a` — there, the annotation built a narrow
promise tied to only one of the two inputs. The same boundary is tested here with a new
example, this time with a situation where both inputs can genuinely be returned: a function
that takes two `Trace` references and returns the slice of whichever name is longer is
rejected by the compiler if written without an annotation, because the compiler cannot
determine which input is returned. Depending on a runtime comparison, the body can return
either `x` or `y` — which shows that the inputs are of equal weight, with neither taking
priority over the other. The accepted form ties both to the same life with an explicit `'a`.

```rust
// non_elided.rs — a signature elision does not cover: it does not compile without the annotation, it compiles once written.

struct Trace {
    name: String,
}

fn longer_name<'a>(x: &'a Trace, y: &'a Trace) -> &'a str {
    if x.name.len() >= y.name.len() { &x.name } else { &y.name }
}

fn main() {
    let a = Trace { name: "short".to_string() };
    let b = Trace { name: "a-longer-name".to_string() };
    println!("{}", longer_name(&a, &b));
}
```

```
a-longer-name
```

The unannotated form of `longer_name` matches none of the three rules: there are two inputs,
and no `self`. The compiler rejects the signature because it cannot determine whether the
returned reference comes from `x` or from `y`; the accepted form applies the same solution
seen in the previous lesson's `other_name` function — tying both inputs to the same `'a`. The
run above works with this accepted form and compares the two names, returning the longer one.

The difference between `other_name` and `longer_name` is not in the grounds for acceptance,
but in **when** the return decision is settled: in `other_name`, which parameter would be
returned was fixed in the function's body; in `longer_name`, that decision is made fresh on
every call, by a runtime comparison. In both cases the annotation does the same job — it
covers every source that could possibly be returned in advance — because which branch will be
chosen is not known at compile time; the annotation has to cover every possible path, not
just the one actually taken.

**LI8.** Elision rules are a **convenience**, not a guarantee that the language can compile
every signature. When none of the three rules fits a signature, the annotation is mandatory
again; the rules only determine **when it is not needed**, not that it will never be needed.

This boundary rests on the same root as the principle established in the previous lesson: the
compiler only skips an annotation when the source yields exactly **one correct reading**. In
a function with two equally weighted inputs like `longer_name`, there is no single reading —
`x` and `y` are of the same type, in the same position, and either can be returned. None of
the three rules contains an assumption that would resolve this ambiguity, because any
assumption could reject some correct programs while accepting some incorrect ones. Rather than
take that risk, Rust rejects the code and leaves the decision to the developer; this shows why
the annotation stays mandatory exactly where it functions as a guarantee, not a convenience.

#### Summary

- Rust's elision rules make writing a lifetime annotation unnecessary for some signature
  forms; two of these were measured in this course: single-input functions and methods that
  take `self`.
- In a single-input function, the elided input's life is assigned to the elided output; in a
  method taking `self`, this assignment is always made to `self`, regardless of the number of
  inputs.
- The explicit and implicit forms of the same signature gave exactly the same behavior in
  both measured cases; in both forms, the values ended **at scope end**.
- This lesson differs from the Go Fundamentals course's axis, which measured unwritten type
  information: what is measured here is where a reference's life ends, not where type
  information is completed from.
- In signatures the three rules do not cover (multiple inputs, no `self`), the annotation
  stays mandatory; a signature outside their coverage is rejected if written without one.
- Elision applies in cases where the compiler derives **exactly one correct reading** from a
  signature; when more than one equally weighted possibility exists, the compiler rejects the
  code rather than make an assumption.

#### Next Step

This lesson showed which signatures elision covers and which it does not — the annotation
was unnecessary in the ones it covers, still mandatory in the ones it does not, and this
necessity was established between a function's parameters. But the same need can also arise
in a struct's own field: if a struct holds a reference, the struct itself cannot outlive the
data it refers to, and this condition also needs to be written with an annotation. The next
lesson measures this condition by comparing two designs that hold the same data, one by
moving it into a struct, the other by borrowing it.

### References in Structs

Source: https://academia.sh/en/courses/ownership/references-in-structs

Measures the condition that holding a reference in a struct's field places on the struct's own life: a struct cannot outlive the data it refers to. The same data is held both by moving it into a struct and by borrowing it into one; it is compared by running the code that a moved field ends together with the struct, while borrowed data ends after the struct.

The previous lesson measured which signatures elision covers and which it does not among
function parameters. But an explicit annotation can be required not only in function
signatures but also in a struct's own definition: if a struct's field holds a reference, the
struct itself becomes tied to that reference's life. This lesson measures this condition by
comparing two designs that hold the same data in a struct through two different paths — by
moving it and by borrowing it.

The question the Go curriculum's Concurrency course asks is different here too: there, a
running unit's stopping was measured; here, the end point of a field inside a struct is
measured. The four end classes established in the Stack and Heap lesson show up again here,
but this time compared across two different field kinds.

Throughout this topic the thing measured has been the same question: where is a reference's
validity range read from? The Lifetime Concept lesson measured this range for a reference, and
the Explicit Lifetime Annotations and Lifetime Elision Rules lessons measured it for a
function signature. This final lesson carries the same question into a struct definition: how
does a struct's **own** life relate to the life of a reference it holds.

#### Two Paths: Moving and Borrowing

When a struct wants to put a value into one of its fields, it has two options: it can **move**
the value (the field becomes the owner) or it can hold a **reference** to the value (the field
only borrows it). The move rule seen in the Ownership and Moves lesson applies to the first
option — the field behaves like any ordinary owned value on its own. The second option opens a
new question: how long can the struct itself live, can it live longer than the data it refers
to?

The answer is no, and this is a rule the compiler makes mandatory in a struct definition. If a
struct's field holds a reference, the struct's own definition has to carry a lifetime
parameter — this parameter relates how long the struct itself can live, at most, to the data
it refers to.

This is the struct-definition counterpart of the rule established for function signatures in
the previous two lessons. There, a function had to say which input its returned reference was
tied to; here, the struct itself has to say which source the reference it holds is tied to.
The difference is where the relationship is established: not in the life of a function call,
but in the life of a struct instance. No matter how long that instance lives — in a variable,
in another struct's field, inside a collection — the guarantee that it cannot outlive the
source of the reference it holds does not change.

#### Measurement: A Moved Field Ends With the Struct, Borrowed Data Ends After the Struct

The measurement below defines two structs: `OwningStruct` owns a `Trace` value,
`BorrowingStruct` only holds a reference to a `Trace` value and, for that reason, carries an
`<'a>` parameter. Both structs operate on the same core (`Trace`, `Ledger`, `Drop`); the only
difference measured is whether the field is moved or not.

```rust
// reference_in_struct.rs — comparing the end point of a moved field in a struct with that of a borrowed field.

use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn read(ledger: &Ledger) -> Vec<String> {
    ledger.borrow().clone()
}

fn newly_added(ledger: &Ledger, before: usize) -> String {
    let all = read(ledger);
    all[before..].join(", ")
}

struct OwningStruct {
    content: Trace,
}

struct BorrowingStruct<'a> {
    content: &'a Trace,
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));

    // Moved field: ends when the struct ends.
    let s0 = read(&ledger).len();
    {
        let moved = Trace::new("moved-field", &ledger);
        let instance = OwningStruct { content: moved };
        println!("field inside OwningStruct -> {}", instance.content.name);
        println!("ledger while OwningStruct stands -> [{}]", newly_added(&ledger, s0));
    }
    println!("once OwningStruct ends  -> {}", newly_added(&ledger, s0));

    // Borrowed field: the struct ends, the data still lives.
    let o0 = read(&ledger).len();
    {
        let borrowed_data = Trace::new("borrowed-data", &ledger);
        {
            let instance = BorrowingStruct { content: &borrowed_data };
            println!("inside BorrowingStruct  -> {}", instance.content.name);
        }
        println!("once BorrowingStruct ends -> [{}]", newly_added(&ledger, o0));
    }
    println!("once borrowed-data ends -> {}", newly_added(&ledger, o0));
}
```

```
field inside OwningStruct -> moved-field
ledger while OwningStruct stands -> []
once OwningStruct ends  -> moved-field
inside BorrowingStruct  -> borrowed-data
once BorrowingStruct ends -> []
once borrowed-data ends -> borrowed-data
```

In the `OwningStruct` section, the `moved` value **moves** into `instance`'s field, and from
that point on it has no binding of its own — it is reachable only through `instance.content`.
While `instance` stands, the ledger is empty; once `instance`'s scope closes, `moved-field`
drops into the ledger. This repeats the "value moved into a struct field" pattern measured in
the Ownership and Moves lesson: the field ends together with the struct itself.

In the `BorrowingStruct` section, `borrowed_data` stays in its own binding and `instance` only
holds a reference to it. When `instance`'s own inner scope closes, the ledger is **still
empty** — the end of the `BorrowingStruct` instance produces no record at all, because
`&'a Trace` does not implement `Drop`. `borrowed_data` itself only drops into the ledger once
its own scope (the outer block) closes — and that is **after** the `BorrowingStruct` instance
ends.

What the two sections have in common is that only one record is produced in each: the sole
record in the `OwningStruct` section is `moved-field`, the sole record in the
`BorrowingStruct` section is `borrowed-data`. `BorrowingStruct` itself never drops into the
ledger — because the struct does not implement `Drop`, it only looks, through the reference it
holds, at a `Trace` value living elsewhere. This shows that a struct holding a reference has
no observable side effect from its own end; the only thing observable is the end of the data
it refers to, and that end never comes before the struct's own end — at the earliest, the two
can coincide by ending together in the same scope.

**LI9.** A value moved into a struct's field ends together with the struct itself. The data
behind a reference held in a struct's field, by contrast, ends **after** the struct — the
struct itself never outlives its own data, but the data it refers to can, and usually does,
outlive the struct.

#### The Struct's Own Method Is Still Written With Elision

Although `BorrowingStruct<'a>` carries a lifetime parameter, the methods written on it are not
necessarily required to repeat that parameter. The elision rules established in the previous
lesson also apply here: the reference returned by a method taking `&self` is tied to `self`'s
own life by the third rule, and this makes writing `'a` separately unnecessary. This is a
concrete example showing that the two lessons are not independent of each other — the same
rules are applied again and again in different contexts.

```rust
// method_elision.rs — a method on a struct carrying a lifetime parameter is still written with elision.

use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

struct BorrowingStruct<'a> {
    content: &'a Trace,
}

impl<'a> BorrowingStruct<'a> {
    // Without annotation: elision's third rule (self) applies here too.
    fn name(&self) -> &str {
        self.content.name.as_str()
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    let data = Trace::new("shared-data", &ledger);
    let instance = BorrowingStruct { content: &data };
    println!("{}", instance.name());
}
```

```
shared-data
```

The `impl<'a> BorrowingStruct<'a>` block has to introduce `'a` — the struct definition
requires it — but the `fn name(&self) -> &str` signature on its own carries no annotation at
all. The compiler behaves here just as it did in the previous lesson: since `&self` is the
single reference input, the third rule applies, and the returned `&str` is tied to `self`'s
life. Because `self` is itself a reference to `BorrowingStruct<'a>`, the returned slice also
stays, indirectly, within `'a`'s bounds — but this relationship is never written in the `name`
method's own signature, because elision already covers it.

This distinction shows the difference between where `'a` is defined and where it is **used**.
`'a` has to appear in the struct's definition and in the `impl` block's header, because the
field itself is tied to it. But if a method's returned value is always reached through `self`,
that method's own signature behaves correctly without writing `'a` again — the elision rule
here works through a single input (`self`) and does not concern itself with `'a` at all. The
place where `'a` would need to be written explicitly would be if a method's returned reference
were tied to a source **other than** `self` (for example, another reference given to the
method as a separate parameter) — which means the previous lesson's limiting measurement
applies here too.

**LI10.** A struct definition carrying a lifetime parameter does not mean every method on
that struct must also carry an explicit annotation. Elision rules operate on struct methods
too; the parameter is needed only in the struct's **definition**, for the field itself.

#### Limiting Measurement: A Struct Definition Cannot Be Written Without a Lifetime Parameter

The `BorrowingStruct` definition cannot be written without the `<'a>` parameter. A field
written as `content: &Trace`, with no lifetime specified at all, is rejected by the compiler,
because the struct definition nowhere says which source the reference is tied to or that the
struct will not outlive that source. This is the struct-definition counterpart of the pattern
seen in the previous two lessons: a reference cannot exist without its relationship being
written into the source.

Elision rules do not come into play here at all — the three rules seen in the previous lesson
were defined only for elided references in function signatures, not for struct fields. A
struct's field is neither an input nor an output; a struct is not a signature re-evaluated on
every call, it is a data layout that stands on its own. There is no "single correct reading"
the compiler could assume here either: if a field holds a reference, that reference's source
has to be **outside** the struct, and who that source is, is information only the person
writing it can know.

The accepted form is the `BorrowingStruct<'a>` above — the `<'a>` parameter is added to the
struct itself, the field becomes `&'a Trace`, and the compiler verifies this relationship: no
instance of `BorrowingStruct` can outlive the data `content` points to. The measurement above
stayed within this boundary — `instance` always ended before `borrowed_data`, never after.
Suppose the `instance` value had been moved outside its own inner scope, returned, or assigned
to a variable in the outer scope: that would not compile, because the reference the instance
holds cannot outlive `borrowed_data` — the restriction is the struct-field form of the
boundary run in the Borrow Checker lesson.

**LI11.** If a struct has a field carrying a reference, it cannot be defined without a
lifetime parameter. Once the parameter is added and the relationship is written, the struct
becomes usable, but no instance of that struct can outlive the data it refers to — the
parameter does not remove this boundary, it only writes it into the source.

#### Two Designs, One Design Decision

The choice between `OwningStruct` and `BorrowingStruct` is not a question of correctness, it
is a design decision. A moved field makes the struct independent of the data: once
`OwningStruct` is built, it can be moved, returned, or stored for a long time, without caring
whether the data lives anywhere else. The cost of this is a copy or a move — the data now
belongs only to that struct.

A borrowed field, by contrast, makes sharing the data possible: the same `Trace` value can be
used both in its own binding and read through `BorrowingStruct`, without producing a copy at
all. The cost of this is that the struct's own life becomes tied to the data — since
`BorrowingStruct` cannot outlive its data, using it as a return value or storing it for a long
time is generally more restricted. Which design to choose depends on how long the struct will
live and whether the data will also be used elsewhere.

There is no third path between these two options — a field either owns or it borrows, there
is no middle ground. A field can also have write access to the data it borrows
(`&'a mut Trace`), but this still falls into the borrowing category: the struct still does not
own the data, it only holds a reference carrying permission to modify it, and it is still
subject to the same lifetime condition. The shared-ownership tools seen further on in the
course (heap-allocated single ownership, reference counting) may look like a third path
standing between these two options, but they are not — they too are, at bottom, a form of
ownership, only how the ownership rule is applied differs. Holding a reference remains the one
case where the data **never** passes into the struct.

#### Summary

- If a struct's field holds a reference, the struct definition must carry a lifetime
  parameter; a definition written without this parameter is rejected.
- A value moved into a struct's field ends together with the struct itself: its life ends
  **where it was moved**.
- The data behind a reference held in a struct's field ends after the struct itself — a
  struct can never outlive its data.
- The lifetime parameter does not remove this boundary, it only writes it into the source;
  even once the parameter is added, a struct instance cannot outlive the data it refers to.
- The choice between a moved field and a borrowed field is a design decision: one makes the
  struct independent of the data, the other makes sharing the data possible.
- The lifetime parameter is required only in the struct's definition; the methods of a struct
  carrying the parameter may need no annotation of their own, as long as elision rules apply.

#### Next Step

Throughout this course's first two topics, a value either moved into a struct or was borrowed
by one, and in both cases that value had **a single owner** — the owner of a moved field was
the struct, the owner of borrowed data was its own scope; even in the borrowed case, ownership
was never split, only shared temporarily. The next topic removes this assumption: can a value
have more than one owner, and if so, what does "ending" mean? Beginning with heap-allocated
single ownership, this topic first measures a single heap ownership that makes recursive types
possible, then measures genuinely shared ownership through reference counting.

## Shared Ownership

How heap-allocated single ownership makes a self-referential type possible, how a counter determines a shared value's lifetime, that same counter's atomic form across threads, borrow checking's move to runtime, and how two values holding onto each other keeps a lifetime from ever ending.

### Heap-Allocated Single Ownership

Source: https://academia.sh/en/courses/ownership/heap-allocated-ownership

How a value can still have exactly one owner while it lives on the heap, how a value that ends together with its container depends on the container's own order, and how that single ownership makes a self-referential type possible.

The References in Structs lesson closed by measuring that a struct either moves a piece of
data or borrows it, and that in both cases exactly one owner exists. This lesson's question
targets that very assumption: can a value have more than one owner? The answer itself is not
coming yet — first it is not the **number** of owners that changes, but **where** a
single-owned value sits. This intermediate step is necessary, because understanding how
shared ownership will be measured first requires knowing how a single-owned value behaves
on the heap. The measurement axis does not change: it still asks where a value's life ends,
not how a running unit stops the way the Go curriculum's Concurrency course asks.

This course's opening lesson treated whether a value sits on the stack or the heap as a
third repetition — after How Computers Work and Production Go: Testing, Tooling and
Performance; memory theory closed there and is not reopened here. That same lesson built
four classes: a value ends at scope end, where it was moved, with an explicit call, or
together with its container. This lesson asks: when a value sits on the heap, which of
these four classes does it fall into, and does the ownership rule still hold? `Box` is a
container that moves a value onto the heap without ever changing the ownership count — that
is the first thing this lesson measures.

#### The Container Is a Class Too, But What Is Inside Is Singular

Ending "together with its container" does not come in a single form. A list carries several
values at once; `Box` carries exactly one. Both fall into the container class, in both the
content ends together with the container, but the container's own end order is not the same.

This lesson's measurement also uses the `Trace`/`ledger` rig set up at the course's opening:
every `Trace` value appends its own name to the ledger when it is dropped, the `Drop`
implementation that makes this possible is not rewritten here, only excerpted. The run below
holds a three-element list and a single `Box` value in the same scope, and prints the order
the ledger receives as the scope closes.

**SO1.** — the claim that "the single value on the heap ends before the list" is confirmed
by the `end order` line of the run below: the name `on-heap-single` comes before the list's
three elements.

```rust
// container_classes.rs — the class of ending with a container: a list and a single value on the heap
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    {
        let mut list = Vec::new();
        list.push(Trace::new("list-1", &ledger));
        list.push(Trace::new("list-2", &ledger));
        list.push(Trace::new("list-3", &ledger));
        let _on_heap = Box::new(Trace::new("on-heap-single", &ledger));
        println!("ledger length while the container is full: {}", ledger.borrow().len());
    }
    let record = ledger.borrow();
    println!("end order: {}", record.join(", "));
}
```

```
ledger length while the container is full: 0
end order: on-heap-single, list-1, list-2, list-3
```

When the scope closes, bindings end starting from the one bound last: `_on_heap` was bound
after `list`, so it is the first to end. `list` ends next, but the three `Trace` values inside
the list end in the **order they were added**. The end order of bindings and the end order
of a container's elements are not the same rule — one follows the reverse of declaration, the
other follows insertion order; because `Box` is a single-element container, this second rule
never shows up in it — only the first rule ever applies.

#### Living on the Heap Does Not Change How a Value Is Used

A value sitting on the heap does not change how it is accessed. A struct's fields and methods
inside a `Box` are reached as if the value sat right there; no separate step is written to
open the container in between.

```rust
// box_access.rs — access through a box works as if the value were right there
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Box::new(Point { x: 3, y: 4 });
    println!("x: {}, y: {}", point.x, point.y);
    let total = point.x + point.y;
    println!("total: {}", total);
}
```

```
x: 3, y: 4
total: 7
```

The lines `point.x` and `point.y` are written the same way whether `point`'s type is `Point`
or `Box<Point>`. The run below compares this directly: the same `Point` value is read once
without being boxed, and once boxed.

```rust
// access_comparison.rs — boxed and unboxed access are written on the same line
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let direct = Point { x: 1, y: 2 };
    let on_heap = Box::new(Point { x: 1, y: 2 });
    println!("direct: x={}, y={}", direct.x, direct.y);
    println!("on-heap: x={}, y={}", on_heap.x, on_heap.y);
}
```

```
direct: x=1, y=2
on-heap: x=1, y=2
```

The only difference between the two lines is how the value was built (`Point { ... }`
versus `Box::new(Point { ... })`); the field-reading lines are identical. Living on the heap
only decides **where** a value is held; it does not change how the value is read and used.
This is a pattern the shared containers ahead will keep following too: whatever the
container's type, access to the value inside it is always written on the same line.

#### Boxing a Value Does Not Multiply Ownership

The Ownership and Moves lesson measured that a value moved into a function, into an inner
scope, or into a struct's field carries its end point along with it, but never changes its
count. The same principle holds for a value on the heap: no matter how many times a `Box`
value changes hands, the `Trace` it carries ends only **once**.

The run below passes a single `Box<Trace>` value through two separate function calls and
leaves it in the last owner's hands.

**SO2.** — the claim that "even after three changes of hands, exactly one end record is
produced" is confirmed by the `total record count: 1` line of the run below.

```rust
// box_move.rs — moving a boxed value does not change the single ownership count
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn to_another_scope(received: Box<Trace>) -> Box<Trace> {
    // Ownership is taken over here, and the same value is returned.
    received
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    {
        let first = Box::new(Trace::new("single-owner", &ledger));
        let second = to_another_scope(first);
        let third = to_another_scope(second);
        println!("ledger length after three moves: {}", ledger.borrow().len());
        println!("final owner: {}", third.name);
    }
    let record = ledger.borrow();
    println!("total record count: {}", record.len());
    println!("records: {}", record.join(", "));
}
```

```
ledger length after three moves: 0
final owner: single-owner
total record count: 1
records: single-owner
```

The binding named `first` moves into the `to_another_scope` function, comes back out under
the name `second`, then moves into the same function again and comes back as `third`. Three
bindings, three separate names, but all of them are the single owner of the same `Trace`
value at different times. The ledger's length being zero as `third` ends shows that **neither**
of the two earlier moves produced an end: only when the final owner's scope closes does a
record open. A single `single-owner` record appearing in the ledger confirms that boxing
does not **multiply** ownership — moving only updates who owns the value, it does not open a
new record.

This result might not seem unexpected at first, but thinking through the opposite case makes
the distinction clear: if ownership multiplied, every call to `to_another_scope` would
produce a separate end record in the ledger, and three records total would appear. The
"single owner" rule established for a value on the stack in the Ownership and Moves lesson
is not broken when the value moves onto the heap; what moves is not the value itself, only the
right to own that value.

#### A Box Can Also End Early With an Explicit Call

A value on the heap does not always have to wait for its container to close. The Borrowing
lesson measured that an explicit `drop` call ends a value's life early; the same call works
on a `Box` too. This shows that the difference between the "with its container" and "with an
explicit call" classes comes **not from the type**, but from how the value is ended.

**SO3.** — the claim that "the value dropped early ends before the value that stays in
scope" is confirmed by the `end order` line of the run below.

```rust
// early_drop.rs — a boxed value can be dropped early with an explicit call
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    {
        let early = Box::new(Trace::new("early-dropped", &ledger));
        let remaining = Box::new(Trace::new("scope-retained", &ledger));
        drop(early);
        println!("ledger length after the explicit call: {}", ledger.borrow().len());
        println!("remaining is still readable: {}", remaining.name);
    }
    let record = ledger.borrow();
    println!("end order: {}", record.join(", "));
}
```

```
ledger length after the explicit call: 1
remaining is still readable: scope-retained
end order: early-dropped, scope-retained
```

Even though `early` is bound before `remaining`, it ends without waiting for scope end;
`remaining` follows the ordinary scope-end rule. The same type — `Box<Trace>` — can fall
into two different end classes; what decides the class is not the container's type, but what
is done to that value in the source.

When this lesson's three measurements come together, a `Box` value can be seen entering
three of the four classes built at the course's opening: if its scope closes naturally, it
ends with its container (the Container Is a Class Too section); if it moves into a function
or another binding, its end point moves with it (the Boxing a Value section); if it is
dropped with an explicit call, it ends with an explicit call (this section). It can never
fall into the scope-end class alone, because a value on the heap is always either inside a
container or the subject of a move — it never stands on its own the way an ordinary binding
on the stack does.

#### A Recursive Type: The Door the Heap Opens

The Data Structures course established self-referential structures such as linked lists and
trees as a concept; this lesson does not re-teach that structure, it only measures that
heap-allocated single ownership **makes** such a type possible.

A definition where each link of a chain carries the next link of its own type directly —
without a container — is rejected by the compiler: when each link contains the next one
directly, the type's own size is never considered settled, because the number of links can
never be read from the source as a fixed bound. This is a different problem from a function
calling itself: function recursion proceeds step by step at runtime, but a type's definition
must be read in a single pass at compile time — a definition that keeps unfolding inside
itself can never finish that reading.

The rule's plainest form can be summed up like this: a definition that writes the `Record`
type directly into a `Record` struct's `next` field is never accepted, because `next` is
again a `Record`, and this definition never closes inside itself. A definition that writes
`Box<Record>` into the same field is accepted, because `Box` is a separate container of
fixed shape that lives on the heap — the definition places not a direct `Record` inside
`Record`, but the fixed-shape container itself. The accepted form carries the next link the
same exact way: not directly, but through a `Box`, and that is why the chain can run as long
as it needs to.

**SO4.** — the claim that "the chain's end order proceeds in the same order it was built" is
confirmed by the `end order: link-a, link-b, link-c` line of the run below — the order does
not follow the reversal seen in binding ends.

```rust
// chain.rs — the door the box opens: a type that holds itself inside itself
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

enum Chain {
    Empty,
    Node(Trace, Box<Chain>),
}

fn length(c: &Chain) -> usize {
    match c {
        Chain::Empty => 0,
        Chain::Node(trace, next) => {
            println!("visited link: {}", trace.name);
            1 + length(next)
        }
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    {
        let chain = Chain::Node(
            Trace::new("link-a", &ledger),
            Box::new(Chain::Node(
                Trace::new("link-b", &ledger),
                Box::new(Chain::Node(
                    Trace::new("link-c", &ledger),
                    Box::new(Chain::Empty),
                )),
            )),
        );
        println!("chain length: {}", length(&chain));
    }
    let record = ledger.borrow();
    println!("end order: {}", record.join(", "));
}
```

```
visited link: link-a
visited link: link-b
visited link: link-c
chain length: 3
end order: link-a, link-b, link-c
```

This order might look surprising at first, because the rule measured earlier in this lesson
was that bindings end starting from the one bound last. But here there is only a single outer
binding — `chain` — not three separate bindings. When `chain` ends, its own `Trace` field
ends first (`link-a`), then its own `Box<Chain>` field ends; that `Box`, in turn, ends its
own inner value first by ending its `Trace` field (`link-b`), then its own `Box`, and so on.
Because the end is a single chained call proceeding from outside in, it proceeds in the same
direction as construction — a different end shape from the list example in the Container Is
a Class Too section, where there were many independent bindings; here there is a single
nested chain of bindings.

When the four measurements come together, one picture emerges: `Box` is a container that
changes **where** a value sits, but never changes **how many owners** it has. Access to the
value stays the same, moving carries the end point but does not multiply the count, an
explicit call can end it early, and it is this single ownership that lets a type carry the
next link of its own kind. The next question's ground is laid right here: in none of these
four measurements did the ownership count ever exceed one — it was always a single binding,
at a single time, owning that value.

#### Summary

- A value has exactly one owner even while it sits on the heap; `Box` does not change that
  count.
- A single value inside a container and the values in a list follow different end-order
  rules: one follows the reverse of declaration, the other follows insertion order.
- Living on the heap does not change how a value is read and used.
- A value on the heap produces exactly one end record in the ledger even when it moves.
- The same type can fall into either the "with its container" or "with an explicit call"
  class, depending on what is done to it in the source.
- Heap-allocated single ownership makes a self-referential type compile; a chain type's end
  order can then proceed in the same direction as its construction.

#### Next Step

In this lesson a value on the heap kept a single owner throughout — whether it moved, was
dropped early, or was chained. The next lesson lifts that assumption: a mechanism where
several bindings can own the same value at once — and how that sharing is tracked with a
counter readable from the source.

### Reference Counting

Source: https://academia.sh/en/courses/ownership/reference-counting

How an Rc value's counter rises with every clone and falls with every end, and how the value reaches the ledger only once the counter hits zero, is measured; that taking a reference never changes this counter, and that what decides the end is not which copy is dropped but how many remain, is shown.

The previous lesson measured that a value on the heap always had exactly one owner —
whether it moved, was dropped early, or was chained. This lesson's question lifts that very
assumption: can a value have more than one owner? The answer is yes — but this yes is not an
evasion, it is a deterministic form of sharing tracked by a counter readable from the
source. What the counter tracks is a value's end point — not how a running unit stops the
way the Go curriculum's Concurrency course measures.

`Rc<T>` is a container that places a value on the heap and counts how many bindings share
access to it. The value lives until this counter drops to zero; the instant the counter
hits zero, the single ownership seen in the previous lesson ends and the value reaches the
ledger. This lesson measures that counter directly, reading what happens on every clone and
every end, and which binding counts as an "owner" and which does not.

#### Why a Reference Is Not Enough

The Borrowing lesson measured that a reference cannot outlive the value it borrows; the
Lifetime Concept lesson named this limit a validity range. This limit falls short for a use
that wants to reach the same value from several places but does not want to stay bound to
the lifetime of the single owning binding in the source: a reference becomes invalid once
its owner's scope closes, no matter how many places read it — the owner itself is still
singular. `Rc` gets past this limit by making a copy itself an owner: what is now involved
is not a borrowing party but a copy that is an owner in its own right, and that copy can
live until the end of its own scope independent of the original binding.

#### Ownership Becomes Countable

When a value is placed on the heap with `Rc::new`, the counter is set to one. When
`Rc::clone` is called, a new copy is produced and the counter rises by one; when one of the
copies ends, the counter falls by one. These copies access the **same** value as one
another — none of them carries its own separate value, all of them share a single `Trace`.

**SO5.** — the claim that "the counter rises with every copy and falls with every end" is
confirmed by the counter values printed in the sequence `1, 2, 3, 2` in the run below.

```rust
// reference_counter.rs — Rc's counter rises with every copy and falls with every end
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    {
        let shared: Rc<Trace> = Rc::new(Trace::new("shared", &ledger));
        println!("counter once built: {}", Rc::strong_count(&shared));

        let second = Rc::clone(&shared);
        println!("counter after one copy: {}", Rc::strong_count(&shared));

        {
            let _third = Rc::clone(&shared);
            println!("counter after two copies: {}", Rc::strong_count(&shared));
            println!("ledger length as the inner scope ends: {}", ledger.borrow().len());
        }
        println!("counter after the inner scope ends: {}", Rc::strong_count(&shared));
        println!("second copy still readable: {}", second.name);
    }
    let record = ledger.borrow();
    println!("record count in the ledger: {}", record.len());
    println!("records: {}", record.join(", "));
}
```

```
counter once built: 1
counter after one copy: 2
counter after two copies: 3
ledger length as the inner scope ends: 0
counter after the inner scope ends: 2
second copy still readable: shared
record count in the ledger: 1
records: shared
```

As the inner scope closes, `_third` ends, but the ledger length stays at zero: `_third`'s end
only drops the counter to two, it does not end the `Trace` value itself. `shared` and
`second` can still access the same value — reading `second.name` still gives the text
`shared`, because the two share the same value. When the outer scope closes, the remaining
two copies end as well, the counter drops to zero, and only at that instant does a single
record appear in the ledger. A single record in the ledger despite three copies shows that
sharing does not **multiply** the value, it only multiplies the number of paths that reach
it.

#### Cloning Is Not Copying Data

In the run above, `Trace::new` is called only **once** — while `shared` is built. `second`
and `_third` are produced with `Rc::clone(&shared)`; this call neither reruns `Trace::new`
nor rebuilds the `name` field. That `second.name` still gives the text `shared` is not a
coincidence: `second` does not carry a separate `Trace` value, it carries a second access
path looking at the **same** value as `shared`. This differs from a `.clone()` call on a
string or a list — there the content is genuinely copied, and changing one afterward leaves
the other unchanged. `Rc::clone`, despite the verb in its name, multiplies not the value it
carries but only the counter and the access to it. This difference produces important
consequences later, when a lesson takes up **modifying** shared data: a change made through
one of the two copies becomes visible through the other one too, because in fact only one
value is being changed.

#### Each Value Has Its Own Counter

If a program has several shared values, each has its own separate counter. A value being
cloned or ending never touches another value's counter.

```rust
// independent_counters.rs — different values' counters are independent of one another
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    let first: Rc<Trace> = Rc::new(Trace::new("first-value", &ledger));
    let second: Rc<Trace> = Rc::new(Trace::new("second-value", &ledger));

    let first_copy = Rc::clone(&first);
    println!("first counter: {}", Rc::strong_count(&first));
    println!("second counter: {}", Rc::strong_count(&second));

    drop(first_copy);
    drop(first);
    println!("ledger (only first has ended): {}", ledger.borrow().join(", "));

    drop(second);
    println!("ledger (both have ended): {}", ledger.borrow().join(", "));
}
```

```
first counter: 2
second counter: 1
ledger (only first has ended): first-value
ledger (both have ended): first-value, second-value
```

`first`'s counter is two because it was cloned once, `second`'s counter is one because it
was never cloned. When both of `first`'s copies end, `second`'s counter still stays at
one — `second` is unaware of that change. The same `ledger` is shared between both values (it
is a recording tool, not the counted value itself), but each `Trace`'s own `Rc` wrapper
keeps its own counter. A program has as many separate, mutually independent counters as it
has separate shared values.

#### Sharing Does Not Violate the Ownership Rule, It Generalizes It

The "single owner" rule built in the previous lesson is not broken here, it is generalized:
the owner is no longer a single binding, it is the **set** of copies that stay active until
the counter drops to zero. The instant the counter hits zero is precisely determined, and
that instant is a record always produced in the same order in the run — not a prediction
depending on what the next step does, but a direct result of the number of copies in the
source. This behaves like an ordinary integer running on a single thread: an `Rc::clone`
call produces a copy and raises the counter, a copy's end lowers it, and both operations run
from the same place — the same scope.

The counter is not limited to individually named bindings; copies produced in a loop follow
the same rule too. The run below collects five copies into a list, then empties the list in
one go.

```rust
// bulk_cloning.rs — copies produced by a loop return to the starting counter once cleared
use std::rc::Rc;

fn main() {
    let shared: Rc<i32> = Rc::new(7);
    let mut copies: Vec<Rc<i32>> = Vec::new();
    for _ in 0..5 {
        copies.push(Rc::clone(&shared));
    }
    println!("counter after five copies: {}", Rc::strong_count(&shared));

    copies.clear();
    println!("counter once copies are cleared: {}", Rc::strong_count(&shared));
}
```

```
counter after five copies: 6
counter once copies are cleared: 1
```

Once five copies are added to the list, the counter rises to six — the originally built
`shared` counts as a copy too. When the list is emptied in one go, the five copies end one
after another and the counter returns to one; because `shared` itself is still held, the
value has not ended yet. This example does not use the `Trace` rig, because what is measured
here is not an end record but that the counter itself follows the same rule under batch
operations too.

#### What Is Counted Is an Owner, Not a Borrow

The counter only counts `Rc::clone` calls. Taking a **reference** to a shared value —
borrowing it to read it — never changes the counter, because a borrowing party is not the
value's owner, it only gains temporary access to it.

**SO6.** — the claim that "taking a reference does not change the counter" is confirmed by
the `counter once built` and `counter after borrowing and reading` lines in the run below
both giving the same value (`1`).

```rust
// borrow_skips_counter.rs — taking a reference never changes the Rc counter
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn read(value: &Trace) -> usize {
    value.name.len()
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    let shared: Rc<Trace> = Rc::new(Trace::new("sample-value", &ledger));
    println!("counter once built: {}", Rc::strong_count(&shared));

    let length = read(&shared);
    println!(
        "counter after borrowing and reading: {} (length {})",
        Rc::strong_count(&shared),
        length
    );

    let copy = Rc::clone(&shared);
    println!("counter after cloning: {}", Rc::strong_count(&shared));

    drop(copy);
    println!("counter once the copy is dropped: {}", Rc::strong_count(&shared));
}
```

```
counter once built: 1
counter after borrowing and reading: 1 (length 12)
counter after cloning: 2
counter once the copy is dropped: 1
```

The `read` function takes `&Trace`, not `Rc<Trace>` itself — so the call never affects the
counter. The counter only rises to two with an `Rc::clone` call and returns to one once the
copy is explicitly dropped. The principle established in the Borrowing lesson — that a
borrowed value does not end — holds here too: a reference never enters the value's ownership
ledger; only copies count as owners, borrowers do not.

This distinction looks small but is open to misunderstanding, because passing `&shared` to a
function through `Rc<Trace>` and passing `Rc::clone(&shared)` look superficially similar —
both can carry a `&` sign. The difference is this: the call `read(&shared)` borrows the
`Rc<Trace>` and automatically coerces down to the `&Trace` inside it, establishing no new
owner; `Rc::clone(&shared)` explicitly produces a new owner copy. The way to tell these two
intentions apart while reading code is to check whether the call carries the name `clone` —
the counter only reacts to a call carrying that name.

#### Not Order, but Count, Decides

The previous lesson measured the **order** in which a container's contents ended — which
value ended before which determined the outcome. With reference counting the situation is
different: it is not which copy is dropped in which order that ends the value, only **how
many** copies remain that decides it.

**SO7.** — the claim that "the value ends when the last remaining copy is dropped, not when
a particular copy is" is confirmed in the run below: even though the originally built copy
of four is dropped in the middle, the ledger stays empty, and only fills when the last copy
goes.

```rust
// order_does_not_matter.rs — with Rc, it is not which copy is dropped but how many remain that matters
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    let shared: Rc<Trace> = Rc::new(Trace::new("ends-when-last-copy-drops", &ledger));
    let a = Rc::clone(&shared);
    let b = Rc::clone(&shared);
    let c = Rc::clone(&shared);
    println!("counter after four copies: {}", Rc::strong_count(&a));

    drop(b);
    println!("counter once the middle copy is dropped: {}", Rc::strong_count(&a));
    println!("is the ledger still empty: {}", ledger.borrow().is_empty());

    drop(shared);
    println!("counter once the originally built copy is dropped: {}", Rc::strong_count(&a));
    println!("is the ledger still empty: {}", ledger.borrow().is_empty());

    drop(a);
    drop(c);
    let record = ledger.borrow();
    println!("record once the last copy is dropped too: {}", record.join(", "));
}
```

```
counter after four copies: 4
counter once the middle copy is dropped: 3
is the ledger still empty: true
counter once the originally built copy is dropped: 2
is the ledger still empty: true
record once the last copy is dropped too: ends-when-last-copy-drops
```

Of the four copies, `b` is dropped, then the originally built `shared` is dropped — a copy
that is neither first nor last by construction order, then the oldest copy itself. Even
after two drops the ledger stays empty, because two copies (`a` and `c`) still remain. The
value ends only once both of these are dropped too. In the previous lesson, a container's
contents ending depended on **the order** they were added in; here the end depends only on
**how many** copies remain — which copy's name is dropped in which order does not change the
outcome. This difference sums up the core distinction between the two container kinds: a
list or a `Box` records the **position** of a single ownership chain and is therefore
sensitive to order; an `Rc` records the **length** of the ownership chain and is therefore
sensitive only to count.

The explicit `drop` call used in the runs above carries forward the same explicit-call idea
seen in the Heap-Allocated Single Ownership lesson, but the outcome differs. There, an
explicit call on a `Box` ended the value itself early — there was no other option, since it
had a single owner. Here the same call only drops **one copy**; the value itself does not
end as long as another copy remains. The same `drop` function means a definite end under
single ownership, and a conditional decrement under shared ownership — its meaning shifts
with the ownership form of the value it is called on.

When the four runs come together, how reference counting generalizes the ownership rule
becomes clear. The counter is always updated from a single place, in a single order; cloning
multiplies access, not data; a reference never enters this count; and what decides the end
is the copies' quantity, not their identity. Together these four properties still provide an
end point that is deterministic and readable from the source — the instant the counter hits
zero shows up in the run just as precisely and repeatably as a single-owned value's scope
end did in the previous lesson.

#### Summary

- `Rc<T>` is a container that places a value on the heap and counts the copies that access
  it.
- The counter rises with every `Rc::clone` call and falls with every copy's end.
- The value reaches its ledger only once the counter drops to zero — its life ends **with its
  container**, at the last remaining copy's end; this instant is readable from the source
  and always produced in the same order in the run.
- Taking a reference does not change the counter; only cloning does.
- The value's end depends not on which copy is dropped but on how many remain.
- `Rc::clone` multiplies not the moved value but the counter and the access to it; copies
  look at the same value.

#### Next Step

The counter measured in this lesson was updated like an ordinary integer, in a single
context: every clone and every end ran from the same scope, in the same order. The next
lesson measures a type that does the same counting job but updates the counter itself in a
different way — the behavior stays the same, only how the counter is updated changes; in
which context this difference is needed stays outside this lesson's scope.

### Atomic Reference Counting

Source: https://academia.sh/en/courses/ownership/atomic-reference-counting

How the same counting mechanism, built with Arc, produces exactly the same end record — with the difference lying not in behavior but in how the counter is updated — is measured; which context it can be used in is not discussed here, since that is the subject of the Concurrency and Asynchronous Rust course. That shared data can only be read through an Arc, never modified directly, is also shown.

The previous lesson measured that reference counting worked with a counter updated like an
ordinary integer in a single context, that this counter rose with every clone and fell with
every end. This lesson measures a type that does the same counting job, but updates the
counter itself in a different way: `Arc<T>`. One limit needs stating up front: **in which
context** this difference is needed — several threads accessing the same value at the same
time — is not this lesson's subject, it is the subject of Concurrency and Asynchronous Rust.
Here only this is measured: does behavior change when the counter is updated atomically, or
does it not? What is measured is still a value's end — not how a running unit stops, the
question the Go curriculum's Concurrency course asks.

This limit's counterpart in the code is plain: no thread is started anywhere in this lesson.
All the runs below, like the previous lesson's, run start to finish within a single flow of
execution. This is not a gap, it is a scoping decision — even though `Arc`'s name evokes a
usage context, what this lesson measures is only the counter itself, not the environment it
is used in. This follows a rule this topic has already applied before: in the
Heap-Allocated Single Ownership lesson, why a piece of writing was rejected was described in
prose, and the accepted writing was run; here too, the context a type's name evokes is not
narrated, only what is measurable — the counter itself — is run.

#### Same Counter, Different Update

`Arc` carries the same interface as `Rc`:

| Rc's counterpart | Arc's counterpart |
|---|---|
| `Rc::new` | `Arc::new` |
| `Rc::clone` | `Arc::clone` |
| `Rc::strong_count` | `Arc::strong_count` |

Each row's left and right sides are same-named calls on two different types doing the same
job: placing a value on the heap and setting the counter to one, producing a copy and
raising the counter, reading the counter's current value. The only difference is how the
increment and decrement operations are carried out. An ordinary increment consists of three
separate steps: the current value is read, one is added, the result is written back. An
**atomic** increment carries out these three steps as a single indivisible operation that
nothing else can interrupt in between. `Rc`'s counter uses an ordinary increment, `Arc`'s
counter uses an atomic increment — but both produce the same mathematical result: one rise
per clone, one fall per end. The same distinction holds for decrementing: a copy's end
lowers the counter by one through either an ordinary or an atomic decrement; whichever
method is used, the counter's final value is the same.

This lesson's measurement reruns the previous lesson's setup with `Arc`, in exactly the same
form.

In this lesson and the previous one, a copy is called not as `shared.clone()` but as
`Rc::clone(&shared)` or `Arc::clone(&shared)`. Both produce the same result — Rust allows a
method call to be written either as `value.clone()` or as `Type::clone(&value)` — but this
lesson prefers the second form throughout, because the first form can give the reader the
impression of "an operation that deep-copies the content". In fact what is cloned here is
only the counter and the access; the `Type::clone(&value)` form makes this clearer in the
source.

**SO8.** — the claim that "the same setup produces exactly the same counter sequence with
`Arc` too" is confirmed by the counter values printed in the sequence `1, 2, 3, 2` in the run
below — the exact same sequence as the previous lesson's `Rc` run.

```rust
// atomic_counter.rs — Arc does the same counting job atomically
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    {
        let shared: Arc<Trace> = Arc::new(Trace::new("atomic-shared", &ledger));
        println!("counter once built: {}", Arc::strong_count(&shared));

        let second = Arc::clone(&shared);
        println!("counter after one copy: {}", Arc::strong_count(&shared));

        {
            let _third = Arc::clone(&shared);
            println!("counter after two copies: {}", Arc::strong_count(&shared));
        }
        println!("counter after the inner scope ends: {}", Arc::strong_count(&shared));
        println!("second copy still readable: {}", second.name);
    }
    let record = ledger.borrow();
    println!("record count in the ledger: {}", record.len());
    println!("records: {}", record.join(", "));
}
```

```
counter once built: 1
counter after one copy: 2
counter after two copies: 3
counter after the inner scope ends: 2
second copy still readable: atomic-shared
record count in the ledger: 1
records: atomic-shared
```

The `Rc::clone(ledger)` call in this run is only used to share the `ledger` that holds the notes;
the sharing being measured is built with `Arc` on `shared`. The counter sequence — one, two,
three, then two — is exactly the same as the previous lesson's. When the inner scope closes,
`_third` ends, the counter returns to two, but the `Trace` value has not ended yet; when the
outer scope closes, the remaining two copies end as well and a single record appears in the
ledger. `Arc` does everything `Rc` does, in the same order, with the same result.

In the last line, reading `second.name` still gives the text `atomic-shared` — just as
`Rc::clone` did in the previous lesson, `Arc::clone` does not recall `Trace::new` either, it
only opens a second access path to the same value. The distinction built in the Cloning Is
Not Copying Data section holds here too: `Arc::clone`, despite the verb in its name,
multiplies not the data but the counter and the access to it. In this run too, `Trace::new`
is called only once; `second` and `_third` look at the same value, at the same heap
location, as `shared`.

#### Same Setup, Same Result

When the two lessons' runs are placed side by side, no difference shows: the counter
sequence is the same, the ledger length is the same, the relationship between a copy ending and
the counter falling is the same. This is not a coincidence — `Arc` is a type carrying the
same contract as `Rc`; both enforce the rule that "the value ends when the counter hits
zero". The difference between them is **not in behavior**, but in the operation that updates
the counter itself: one does an ordinary increment/decrement, the other does it atomically.
In which context this difference gains meaning — several threads accessing the same counter
at the same time — is not taken up here; that question belongs to Concurrency and
Asynchronous Rust.

Two other properties measured in the Reference Counting lesson — that every value has its
own counter, and that the end depends on how many copies remain rather than which one — can
be tested for `Arc` as well. The run below builds two independent `Arc` values, drops one's
three copies in an order other than construction order, and shows the other value is
unaffected.

```rust
// arc_properties.rs — independent counters and order-independence hold for Arc too
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    let first: Arc<Trace> = Arc::new(Trace::new("first-atomic", &ledger));
    let second: Arc<Trace> = Arc::new(Trace::new("second-atomic", &ledger));

    let a = Arc::clone(&first);
    let b = Arc::clone(&first);
    println!(
        "counter after three copies — first: {}, second: {}",
        Arc::strong_count(&first),
        Arc::strong_count(&second)
    );

    drop(a);
    drop(first);
    println!(
        "ledger once two copies are dropped (should still be empty): {}",
        ledger.borrow().join(", ")
    );

    drop(b);
    println!(
        "ledger once the last copy is dropped too: {}",
        ledger.borrow().join(", ")
    );
    println!("second counter still unchanged: {}", Arc::strong_count(&second));

    drop(second);
    let record = ledger.borrow();
    println!("ledger once second is dropped too: {}", record.join(", "));
}
```

```
counter after three copies — first: 3, second: 1
ledger once two copies are dropped (should still be empty): 
ledger once the last copy is dropped too: first-atomic
second counter still unchanged: 1
ledger once second is dropped too: first-atomic, second-atomic
```

When `a` and the original `first` binding of the three copies are dropped, the ledger is still
empty — because `b` remains, `first-atomic` has not ended yet. Once `b` is dropped too, the
ledger fills. Meanwhile `second`'s counter never changes: the two values keep two independent
records. The result is exactly the same as what was seen for `Rc` in the Reference Counting
lesson — only the operation that updates the counter differs. The drop order here was again
chosen as `a`, `first`, `b`, outside the construction order (`first`, then `a`, then `b`);
the result still did not change, because what decides it is not which copy comes in the drop
order, but how many remain.

#### Two Separate Types, a Visible Choice

Rust offering this as two separate types — `Rc` and `Arc` — rather than a single
configurable type is a design decision. Which form the counter is updated in is not a
runtime flag or a setup option, it is a **visible type choice in the source code**: whether
you build a value with `Rc::new` or with `Arc::new` makes readable from the source not who
may or may not read that value, but which operation its counter is updated with. This
repeats a pattern the course has seen many times before: a behavioral difference is tied not
to a runtime decision, but to a type difference. The two types can largely stand in for each
other — they work with the same `new`, `clone`, `strong_count` calls — but which one was
chosen stays in the source as a permanent piece of information about that value.

This arrangement takes the same shape as the distinction seen in the Ordered and Set
Structures lesson of the Rust Fundamentals course: a hash map and an ordered map do the same
basic job too, but the choice between them is not a runtime decision, it is a source
decision tied to which type is written. The choice between `Rc` and `Arc` fits the same
mold — both types give the same answer to the question "how many copies are there", the
only difference is how that answer is produced.

#### The Atomic Counter Protects the Counter, Not the Data

Atomic updating protects only the **counter**; when it is the shared data itself at stake
rather than the counter, `Arc` gives no additional guarantee. A line that tries to write
directly to a field through an `Arc` does not compile: `Arc`, just like a cloned `Rc`, gives
only shared access to the value it holds — the same principle by which a reference (`&T`)
allows only reading. The accepted piece of writing only **reads** the same field.

This constraint is not actually unique to `Arc`; the same rule held for `Rc` in the previous
lesson too, it was just not spelled out there because the actual measurement was the
counter. The reason it is spelled out here is this: an atomic counter can create the
impression that "shared data can now safely be modified", because the word "atomic" evokes a
guarantee. The measurement corrects this impression — the guarantee is only for the
counter's own consistency, not for the content of the value the counter protects. Two copies
can read the same data at the same time, but neither can modify it on its own; the need to
modify it requires a separate tool.

**SO9.** — the claim that "several copies can read the same value but none of them can
modify it" is confirmed by the run below printing the same `value` field from both copies,
and containing no write line at all.

```rust
// shared_data_read_only.rs — only reading is possible through Arc
use std::sync::Arc;

struct Measurement {
    value: i64,
}

fn main() {
    let shared = Arc::new(Measurement { value: 42 });
    let copy = Arc::clone(&shared);

    println!("first read: {}", shared.value);
    println!("read from copy: {}", copy.value);
    println!("counter: {}", Arc::strong_count(&shared));
}
```

```
first read: 42
read from copy: 42
counter: 2
```

Both copies read the same `value` field and both give the same result, because there is in
fact a single `Measurement` value, and both copies access it. No line in this program writes
to the `value` field — that is not a coincidence, reading is the only operation `Arc`
allows. A line trying to set `value` to `43` would not compile; this rejection is not
additionally run in the program, because it is not placed into a source code block that does
not compile. The counter is also read in this run (`counter: 2`), but this line has no
relation to the `value` field — one tells how many copies there are, the other tells the
content of the value the copies look at; they are two independent, separate questions.

This example uses a plain `Measurement` struct instead of the `Trace`/`ledger` rig, because
what is measured is not an end record but access itself. The Heap-Allocated Single
Ownership lesson showed that reading `point.x` through a `Box` works as if the value sat
right there; here too, `shared.value` and `copy.value` use the same line form. The
difference is this: in `Box` this access was open to both reading and writing, because there
was a single owner. In `Arc` the same access is open only to reading, because ownership is
shared and a mechanism that tracks who writes to a shared value and when is not yet in
place.

When the three measurements come together, this emerges: at the counter level, `Arc` is a
strengthened form of `Rc`, but this strengthening never spreads to the shared data itself.
However robustly the counter is updated, whether the data is modifiable is a separate
question, and neither of these two types answers it yet.

Reading this topic's first three lessons together shows a progression. Heap-Allocated
Single Ownership established that a value can sit on the heap while its owner is still
singular. Reference Counting made that singularity extensible with a counter — ownership
was now a countable set. This lesson offered how that counter is updated as a variable
choice, but did not change the ownership and access model itself: however many copies there
are, none of them can modify the value. Across three lessons the container changed, the
counter changed, but the right to write to the value inside never arose — how that right is
gained is the topic's next and final question. What the three lessons share is this: in
each of them a value's lifetime is still readable from the source, none of them breaks this
determinism — only the form of ownership and the right of access widen, step by step.

#### Summary

- `Arc<T>` carries the same interface as `Rc<T>` and enforces the same "the value ends when
  the counter hits zero" rule.
- The end record and counter sequence the two types produce are exactly the same — in both,
  the value's life ends **with its container**; the difference is not in behavior, but in
  the counter being updated atomically.
- In which context the atomic update is needed is not this lesson's subject; it belongs to
  Concurrency and Asynchronous Rust.
- Data shared through `Arc` can only be read; writing directly to a field does not compile.
- The atomic counter protects not the data but only itself; that shared data stays
  unmodifiable is a separate constraint.
- Every `Arc` value has its own counter, and the end depends not on which copy but on how
  many remain — this principle is the same as with `Rc`.

#### Next Step

This lesson measured that a shared value can be read but not modified. The next question is
unavoidable: what happens if a shared value needs to be modified? The Variables and
Immutability lesson left a boundary open: a binding's immutability and a field inside it
being modifiable were separate questions, and that lesson only drew the boundary, it did not
close it. The next lesson — this topic's fourth — closes that boundary by measuring how
borrow checking can move from compile time to runtime, and what cost that move brings.

### Interior Mutability

Source: https://academia.sh/en/courses/ownership/interior-mutability

How the same 'one writer or many readers' rule is tested with a counter at runtime instead of compile time is measured; that a binding itself can stay immutable while the value inside it becomes modifiable through RefCell, and that a failed borrow attempt can be taken as a bool result instead of a panic, are shown. The rule does not disappear, it changes location.

Across the previous three lessons a shared value could always be read; it could never be
modified in `Rc` or `Arc`, while in `Box` modification was already possible because there
was a single owner (if the binding took `mut`). The Variables and Immutability lesson had
also left a question open: it established that a binding not taking `mut` guarantees no
change is allowed **through that binding's own name**, but the question of whether the same
data could be shared through another binding or reference, and if shared, whether
immutability would still hold from there, was left to this course. This lesson answers
exactly that question: is there a way to modify a value both while ownership is shared and
while the binding looks immutable? What this course measures stays the same throughout —
where a value's life ends — not how a running unit stops, the way the Go curriculum's
Concurrency course measures.

The answer is yes, but this change has a cost; this lesson measures that cost, and as the
topic's fourth lesson, brings together the topic's four measurements so far.

The Atomic Reference Counting lesson left this need explicit: a line trying to write to a
field through an `Arc` did not compile, because it had been shown that knowing how many
copies can access a shared value at once is not enough to modify that value safely. The
same limit held for `Rc` — in `Box` modification was already possible because there was a
single owner (if the binding took `mut`), but in `Rc` and `Arc`, writing shut the moment
sharing began, because both gave only shared access to the value they held. In this lesson
that door reopens, but with a different key: not through the type itself, but through a
permission tested at runtime.

#### The Rule Changes Location

The Borrow Checker lesson showed that the rule "either one mutable reference or several
shared references at a time" is tested at compile time: a piece of writing that violates
this rule never compiled at all. `RefCell<T>` is a container that carries the same rule to
**runtime**. Shared access to the value inside it is requested with `borrow()`, mutable
access with `borrow_mut()`; `RefCell` tracks these requests with a counter, and a request
that violates the rule is not left uncompiled, it is **rejected** at runtime.

In this lesson the rejection is measured not with a panic, but with a result returned by
`try_borrow()` and `try_borrow_mut()` calls: `Ok` if the request is valid, `Err` if not. No
panic text is printed anywhere; only whether the request succeeded is printed, as a `bool`.

`RefCell`, just like `Rc` and `Arc`, keeps a counter too — but what it counts is different.
`Rc` and `Arc`'s counter counted how many **owners** there were, and the value ended once it
hit zero. `RefCell`'s counter counts how many **active borrows** there are, and it never
ends the value; it only tracks which kind of access (shared or mutable) is currently
ongoing. These two counters can work independently of each other: an `Rc<RefCell<T>>` value
can hold both how many copies it has and who currently borrows it, at the same time, with
two separate counters. While one copy ends and the ownership counter falls, the borrow
counter is entirely unaware of whether that copy currently held a borrow — the two answer
separate questions.

#### A Binding's Immutability Does Not Bind Its Content

The run below shows this directly: the binding named `counter` does **not** take `mut`, but
the integer inside it changes anyway.

**SO10.** — the claim that "the value inside can change while the binding itself stays
immutable" is confirmed by the run below: even though the line
`let counter: RefCell<i64> = RefCell::new(0);` does not contain `mut`, the line
`value after the first write: 1` is printed.

```rust
// borrow_time_shift.rs — a rule rejected at compile time turns into a result at runtime
use std::cell::RefCell;

fn main() {
    let counter: RefCell<i64> = RefCell::new(0); // binding is not mut

    {
        let mut writer = counter.borrow_mut();
        *writer += 1;
    }
    println!("value after the first write: {}", counter.borrow());

    let first_read = counter.borrow();
    let second_read = counter.borrow();
    println!("two reads together: {} and {}", *first_read, *second_read);

    let write_attempt = counter.try_borrow_mut();
    println!("did the write attempt succeed while reading was ongoing: {}", write_attempt.is_ok());

    drop(first_read);
    drop(second_read);

    let second_write_attempt = counter.try_borrow_mut();
    println!(
        "did the write attempt succeed after the reads were dropped: {}",
        second_write_attempt.is_ok()
    );
}
```

```
value after the first write: 1
two reads together: 1 and 1
did the write attempt succeed while reading was ongoing: false
did the write attempt succeed after the reads were dropped: true
```

The `counter` binding does not carry `mut` anywhere, but the `borrow_mut()` call genuinely
changes the value inside it. The Variables and Immutability lesson established that not
taking `mut` guarantees no change is allowed **through the name itself**; `RefCell` does not
break this guarantee — the name `counter` itself still cannot be rebound, none of its fields
can be changed with an ordinary assignment. `borrow_mut()` enters not through the name
itself, but through a separate gate `RefCell` checks at runtime. While `first_read` and
`second_read` live at the same time, both can read the same value — "many readers" is
permitted. A write attempt arriving while they are still alive fails; the same attempt
succeeds once both are dropped. The rule is the same: one writer or many readers, never
both.

#### Two Writers Violate the Same Rule Too

The rule covers not only "no reading while writing" but also the part that says "no two
writers at the same time". The run below tests this separately: the same rejection happens
when a second mutable borrow is requested while a mutable borrow is already ongoing.

**SO11.** — the claim that "two mutable borrows at the same time are both rejected" is
confirmed by the `did the second write attempt succeed while the first writer was active:
false` line in the run below.

```rust
// two_writers_attempt.rs — two mutable borrows at the same time are both rejected
use std::cell::RefCell;

fn main() {
    let data: RefCell<Vec<i32>> = RefCell::new(vec![1, 2, 3]);

    let mut first_writer = data.borrow_mut();
    first_writer.push(4);

    let second_write_attempt = data.try_borrow_mut();
    println!(
        "did the second write attempt succeed while the first writer was active: {}",
        second_write_attempt.is_ok()
    );
    drop(first_writer);

    let is_it_successful_now = data.try_borrow_mut();
    println!("successful after the first writer is dropped: {}", is_it_successful_now.is_ok());
    drop(is_it_successful_now);
    println!("data length: {}", data.borrow().len());
}
```

```
did the second write attempt succeed while the first writer was active: false
successful after the first writer is dropped: true
data length: 4
```

The second attempt, opened while `first_writer` is still held, is rejected too — this time
not two colliding reads, but two write requests. An element having been pushed to `data`
through `first_writer` (`push(4)`) becomes permanent the instant the guard is dropped: the
length in the last line is four. The counter distinguishes not just how many readers are
active, but also the **kind** of active access — while a writer exists, neither another
writer nor a reader is accepted.

These two runs (the previous section's and this section's) together cover the whole rule:
many readers were permitted, while a reader with a writer, and two writers, were both
forbidden. Two of the three cases were measured by `try_borrow` or `try_borrow_mut`
returning `Err`, the remaining one by returning `Ok`; no panic text was printed in any of
them, no program crashed in any of them. The "modifying while reading" violation rejected
at compile time in the Borrow Checker lesson meets us here under the same name, but as an
`Err` at runtime.

#### The Rule Does Not Disappear, It Changes Location

None of the three rejection examples in the Borrow Checker lesson entered a code block —
all of them were described in prose, with the accepted solution placed next to them; the
violating source never compiled. Here the same kind of violation — another access to a
value being requested while it is being written — genuinely **compiles**; it only produces
an `Err` at runtime. This does not mean the rule has disappeared: an error avoided at
compile time turns into a result at runtime. The write attempt line printing `false` in the
run above shows that a violation of the kind that would never compile in the Borrow Checker
lesson compiles here and is caught at runtime — the program does not crash, because the
rejection is turned into a `Result` by `try_borrow_mut`, instead of a panic.

This trade-off's cost is this: compile-time checking filters out a piece of writing before
it runs; runtime checking only catches the same violation once that line actually runs.
Wrong code no longer fails to compile — instead it reports its own failure while running —
but this report only happens once that line is reached; a run that never hits the violation
never shows the problem at all. The principle measured in the Borrow Checker lesson — "the
checker secures ownership discipline, not correctness" — goes one step further here: runtime
checking, too, only tests the **path that runs**, it cannot see a violation on a line the
code never reaches.

#### The Ledger Itself Is Explained in This Lesson

This was exactly the question the Variables and Immutability lesson left to the Ownership
and the Memory Model course: when the same data is shared through another binding or
reference, does immutability still hold from there? The `ledger` used throughout every lesson
of this course carried the answer to this question implicitly from the start. `ledger` was a
value of type `Rc<RefCell<Vec<String>>>`. The Reference Counting lesson explained the `Rc`
half, this lesson explains the `RefCell` half: `ledger` was shared (`Rc`) and its content was
modifiable (`RefCell`) — using two nested containers provided both sharing and
modifiability at the same time.

**SO12.** — the claim that "a function taking `&Ledger` can still modify its content" is
confirmed by the run below: even though the `add_record` function's parameter is `&Ledger`
(not a mutable reference), the line `record count: 2` is printed.

```rust
// the_ledgers_interior_mutability.rs — the ledger type used throughout this topic worked this way
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace {
            name: name.to_string(),
            ledger: Rc::clone(ledger),
        }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

fn add_record(ledger: &Ledger, text: &str) {
    // The ledger parameter is taken with &, not mut; the content still changes.
    ledger.borrow_mut().push(text.to_string());
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));
    add_record(&ledger, "added-by-hand");
    {
        let _measurement = Trace::new("added-on-drop", &ledger);
    }
    let record = ledger.borrow();
    println!("records: {}", record.join(", "));
    println!("record count: {}", record.len());
}
```

```
records: added-by-hand, added-on-drop
record count: 2
```

The `add_record` function takes `ledger: &Ledger` — that is, `&Rc<RefCell<Vec<String>>>`, an
ordinary shared reference. Despite this, the `ledger.borrow_mut().push(...)` call genuinely
adds an element to the list. This is the very thing every `Trace::drop` call has been doing
since the topic's first lesson: even though the `drop` function takes `&mut self`,
`self.ledger` could not be modified directly because it was an `Rc` — the line
`self.ledger.borrow_mut().push(...)` used not the access `&mut self` itself grants, but the
runtime access `RefCell` grants. Every run since the start of the topic was actually using,
in the background, the very mechanism this lesson measures — only this lesson names that
mechanism explicitly.

This also explains why `ledger` is defined as `Rc<RefCell<Vec<String>>>` rather than plain
`Vec<String>`. `Vec<String>` alone would be neither shareable nor modifiable from several
places — a function would need to either own it or be given an ordinary reference to it,
and calling `push` through an ordinary reference would require `&mut`. `Rc` brought
sharing, `RefCell` brought modifiability; together, even though only `&Ledger` was passed
every time `Trace::new` was called, every `Trace` could write its own end to the same list.
This topic's first three lessons only took up the `Rc` half of this nested container; this
lesson completes the other half.

#### Access Is Not Transparent, It Requires an Explicit Call

The Heap-Allocated Single Ownership lesson showed that fields inside a `Box` can be
accessed directly: the writing `point.x` was the same whether the value was on the heap or
not. In the Atomic Reference Counting lesson, `shared.value` could also be read directly.
`RefCell` does not carry this transparency forward: the value inside it cannot be reached by
writing `cell.field` directly, `borrow()` or `borrow_mut()` must first be called to obtain a
guard value. This difference is not a coincidence — `Box`, `Rc`, and `Arc` knew at compile
time that access to the value inside was **always safe**, so they could hide that access.
`RefCell`, on the other hand, tests safety only at runtime, on every single call; this test
needs to be **visible** in the source, because the test can fail too.

This visibility takes concrete form in the `Result` value `try_borrow_mut` returns: while
writing to a `Box` field is an operation that never fails, writing to a `RefCell` is an
operation that is attempted and whose **result must be checked**. The code cannot hide this
difference — the calling side is forced to handle, on the very next line, whether its
request was accepted. This is the final step of a pattern the topic has followed across
three measurements: when an operation is certain and when it is conditional can be read
from the type that operation returns — `()` always succeeds, `Result` may succeed or may
fail.

With these four measurements together, the full picture of the topic emerges.
Heap-Allocated Single Ownership established that a value can sit on the heap while its owner
stays singular. Reference Counting opened that singularity into a countable plurality.
Atomic Reference Counting showed that this counting gives the same result in different
contexts too, but that shared data still stays unmodifiable. This lesson removed that last
constraint, at a cost: modification is now possible, but every attempt now returns a
result, and that result has to be checked. At no step across the topic was determinism
lost — every end, every counter change, every borrow attempt was produced in the same order,
with the same result, in the run.

#### Summary

- `RefCell<T>` is a container that tests the "one writer or many readers" rule with a
  counter at runtime instead of compile time; the value inside it ends its life **with its
  container**.
- Even when a binding itself is not `mut`, the value inside it can change through
  `RefCell`; the name itself still cannot be rebound, the change passes through a separate
  gate.
- The same data can also be modified with `RefCell` when shared across several bindings
  (`Rc` copies); this answers the question the Ownership and the Memory Model course left
  open about "what happens to immutability under sharing" — the `ledger` type used since the
  start of the course was exactly this mechanism.
- A failed borrow attempt is measured not with a panic, but with a result returned by
  `try_borrow`/`try_borrow_mut`.
- The rule does not disappear, it changes location: a violation avoided at compile time
  turns into a result (`Err`) at runtime.
- Access through `RefCell` is not transparent; every access requires an explicit `borrow`
  call, because a call may not always succeed.

#### Next Step

Across these four lessons a value's ownership multiplied, became countable, went atomic, and
finally became modifiable while shared — but one single assumption was never questioned the
whole time: the values being shared were independent of one another, none of them was
**holding onto** another. Every counter counted the copies remaining for its own value; no
copy held the counted value itself backward. The course's final question asks exactly this:
what if two values, through `RefCell`, each place an `Rc` inside the other and end up
holding onto each other? Can the counter still drop to zero then, or can it never drop at
all?

### Reference Cycles

Source: https://academia.sh/en/courses/ownership/reference-cycles

How, when two values hold each other through Rc via RefCell, the strong counter never drops to zero and no record ever appears in the ledger — the course's only leak — is measured. That a weak reference breaks the same reference cycle without raising the counter, and that once broken the ledger produces two records at once, is run; strong and weak counter values are printed.

Across this course's three topics, over thirteen lessons, a value's life always fell into
one of four classes, and a zeroth option was never seen — even `Rc` and `Arc`'s counter,
however many times it was cloned, eventually dropped to zero, sooner or later. This final
lesson measures the one case that breaks that very assumption: if two values, through
`RefCell`, each place an `Rc` inside the other, the strong counter never drops to zero, and
no record ever appears in the ledger. Every end measured across the course's thirteen lessons
had a point that was readable from the source and deterministic; this lesson shows that
determinism's own limit — that an end might never happen at all.

The leak measured in the Go curriculum's Concurrency course was a goroutine that was
expected to stop but never did: a running unit was leaking. Here it is not a running unit
that leaks but a value — the source code itself ends in a completely ordinary way, the
program terminates, but two `Node` values remain in memory after that end, never dropped.

#### When Two Values Hold Each Other

The `Ledger`, `Trace`, and `Drop` implementation from the core is used here with the same
excerpt. On top of it, exactly one new type is added: `Node` carries its own `Trace` and a
`next: RefCell<Option<Rc<Node>>>` field that holds a strong reference to the next node. Two
`Node` values (`a`, `b`) are built with `Rc::new`, then each gives the other a reference
with `Rc::clone` — the very copying operation measured in the Reference Counting lesson,
applied here only in both directions at once.

**SO13.** — the claim that "when two values hold each other, the strong counter never drops
to zero even after the scope closes" is confirmed by the `ledger length once the scope closes:
0` line in the run below.

```rust
// cycle_formation.rs — two values holding each other: the counter never drops to zero, the ledger stays empty
use std::cell::RefCell;
use std::rc::Rc;

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

struct Node {
    trace: Trace,
    next: RefCell<Option<Rc<Node>>>,
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));

    {
        let a = Rc::new(Node {
            trace: Trace::new("node-a", &ledger),
            next: RefCell::new(None),
        });
        let b = Rc::new(Node {
            trace: Trace::new("node-b", &ledger),
            next: RefCell::new(None),
        });

        *a.next.borrow_mut() = Some(Rc::clone(&b));
        *b.next.borrow_mut() = Some(Rc::clone(&a));

        println!("a name: {}, counter: {}", a.trace.name, Rc::strong_count(&a));
        println!("b name: {}, counter: {}", b.trace.name, Rc::strong_count(&b));
    }

    println!("ledger length once the scope closes: {}", ledger.borrow().len());
}
```

```
a name: node-a, counter: 2
b name: node-b, counter: 2
ledger length once the scope closes: 0
```

While the container is full, the counter is two in both: `a`'s own binding is one, the
copy held by `b.next` is one; symmetrically for `b`. When the scope closes, the `a` and `b`
bindings end in the reverse of declaration order — the rule built in the Stack and Heap
lesson holds here too. But each binding's end only lowers its own counter by one: when `b`
ends, the copy held by `a.next` still keeps `b`'s `Node` alive; when `a` ends, the copy held
by `b.next` keeps `a`'s alive too. What remains is a counter of one in both — it never
reaches zero. `Node::trace`'s `Drop` is therefore never triggered, because the `Node` itself
is never dropped; the ledger stays empty until the end of the run.

#### A Weak Reference Does Not Raise the Counter, It Breaks the Reference Cycle

The way to break this reference cycle is to turn one of the two directions into a **weak**
reference: `Weak<T>` points at the same value as `Rc<T>` does, but never raises the strong
counter. `Node` splits into two fields here — `strong_next` keeps one direction strong,
`weak_previous` keeps the other direction weak. Which direction is weak is a design
decision; it is not an answer the language gives on its own.

**SO14.** — the claim that "a weak reference never raises the strong counter" is confirmed
by the `a name: broken-a, strong: 1, weak: 1` line in the run below: `b` holds a weak
reference to `a`, and `a`'s strong counter is still one.

```rust
// breaking_with_weak.rs — a weak reference does not raise the counter, and it breaks the reference cycle
use std::cell::RefCell;
use std::rc::{Rc, Weak};

type Ledger = Rc<RefCell<Vec<String>>>;

struct Trace {
    name: String,
    ledger: Ledger,
}

impl Trace {
    fn new(name: &str, ledger: &Ledger) -> Trace {
        Trace { name: name.to_string(), ledger: Rc::clone(ledger) }
    }
}

impl Drop for Trace {
    fn drop(&mut self) {
        self.ledger.borrow_mut().push(self.name.clone());
    }
}

struct Node {
    trace: Trace,
    strong_next: RefCell<Option<Rc<Node>>>,
    weak_previous: RefCell<Weak<Node>>,
}

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(Vec::new()));

    {
        let a = Rc::new(Node {
            trace: Trace::new("broken-a", &ledger),
            strong_next: RefCell::new(None),
            weak_previous: RefCell::new(Weak::new()),
        });
        let b = Rc::new(Node {
            trace: Trace::new("broken-b", &ledger),
            strong_next: RefCell::new(None),
            weak_previous: RefCell::new(Weak::new()),
        });

        *a.strong_next.borrow_mut() = Some(Rc::clone(&b));
        *b.weak_previous.borrow_mut() = Rc::downgrade(&a);

        println!("a name: {}, strong: {}, weak: {}", a.trace.name, Rc::strong_count(&a), Rc::weak_count(&a));
        println!("b name: {}, strong: {}, weak: {}", b.trace.name, Rc::strong_count(&b), Rc::weak_count(&b));

        let recovered = b.weak_previous.borrow().upgrade();
        println!("is the way back from b to a successful: {}", recovered.is_some());
    }

    println!("ledger once the scope closes: [{}]", ledger.borrow().join(", "));
}
```

```
a name: broken-a, strong: 1, weak: 1
b name: broken-b, strong: 2, weak: 0
is the way back from b to a successful: true
ledger once the scope closes: [broken-a, broken-b]
```

`a`'s strong counter stays at one, because only its own binding owns it; `b`'s weak counter
is one, because `b.weak_previous` looks at it but does not own it. `b`'s strong counter, on
the other hand, is two — its own binding, plus the copy held by `a.strong_next`. The
`upgrade()` call confirms that the weak reference still looks at a valid value: it returns
an `Option<Rc<Node>>`, and since `a` is still alive, the result is `Some`.

**SO15.** — the claim that "once this reference cycle is broken, the ledger produces two
records at once" is confirmed by the `ledger: [broken-a, broken-b]` in the same run's last
line.

When the scope closes, `b` ends first and lowers its strong counter from two to one —
because `a.strong_next` still holds a copy, `b`'s `Node` is not dropped yet. When `a` ends,
its own counter drops to zero and it is genuinely dropped; this drop first writes its own
`trace` field (`broken-a`) to the ledger, then, while dropping its own `strong_next` field,
also drops the last strong reference to `b` — `b`'s counter drops to zero too, and `b` is
dropped as well (`broken-b`). The weak reference never enters this chain: no strong counter
changes while `b.weak_previous` is dropped, because it was never counting one to begin
with.

#### The Limiting Measurement: The Compiler Does Not See the Reference Cycle

The difference between the two runs above passes without any rejection in the source. Both
`cycle_formation.rs` and `breaking_with_weak.rs` compile with the same kinds of lines:
`Rc::new`, `Rc::clone`, writing to a `RefCell` field — the very operations used since the
Reference Counting lesson (SO5). The Borrow Checker lesson had shown that the checker tests
ownership discipline but does not guarantee the code's correctness; here that same limit
widens one step further: the compiler never notices that two `Rc` values hold each other,
because every `Rc::clone` call is, on its own, a valid operation — a counter increment,
permitted regardless of which value it points at.

**SO16.** — the claim that "source that holds itself in a cycle goes through the exact same
compilation path as source that does not" is confirmed by both runs (`cycle_formation`,
`breaking_with_weak`) compiling without any warning or error.

This is not an exception to the determinism measured across every lesson of the course, it
is that determinism's own limit: the end point is still readable from the source, only this
time what is read is that an end will **never take place**. Breaking a reference cycle is
therefore the writer's responsibility, not the compiler's; choosing which direction is weak
is too — had `a` been chosen to hold `b` weakly instead of `b` holding `a` weakly, the same
break would have happened in the opposite direction, and the result would not have changed.

#### Closing Table

The fourteen end points measured across fourteen lessons are gathered below. Each row's
class is written in that lesson's own text; the table does not make a new classification,
it brings together what was scattered.

| Lesson | Measured value | Where the life ends | Limiting measurement |
|---|---|---|---|
| Stack and Heap | `scope-1/2/3` end order | scope end | end order is exactly the reverse of declaration |
| Ownership and Moves | `into-function`, `inner-scope`, `into-struct` | where it was moved | a move is not a copy; a single record in the ledger |
| Borrowing | `borrow-1` (shared + mutable borrow) | scope end | a borrow does not extend a lifetime, a reference cannot outlive its source |
| Slices | `view` (list slice) | scope end | a slice locks not just the range but the whole source |
| The Borrow Checker | `push_and_count` return value | scope end | the checker tests access discipline, not correctness |
| The Lifetime Concept | `r1`, `r2`, `r3` reference range | scope end | two independent ranges need not nest |
| Explicit Lifetime Annotations | `primary_name`, `other_name` | scope end | an annotation is a request; a body that does not keep the promise is rejected |
| Lifetime Elision Rules | `first_three_letters`, `longer_name` | scope end | elision is a convenience; a signature it does not cover requires an explicit annotation |
| References in Structs | `OwningStruct`, `BorrowingStruct` | where it was moved | a struct cannot carry a reference without a lifetime parameter |
| Heap-Allocated Single Ownership | `on-heap-single`, `list-1..3` | with its container | the same type can fall into two different classes; the class comes from the operation |
| Reference Counting | `shared` (Rc) counter: 1, 2, 3, 2 | with its container | what decides the end is not a copy's identity but how many remain |
| Atomic Reference Counting | `shared` (Arc) counter: 1, 2, 3, 2 | with its container | the atomic counter protects not the data, only the counter |
| Interior Mutability | `counter` (RefCell) borrow attempts | with its container | the rule does not disappear, it turns into an `Err` at runtime |
| Reference Cycles | `node-a`, `node-b` (holding each other) | never ended | the compiler does not see it; the same `Rc::clone` builds both the sharing and the leak |

#### Summary

- When two values hold each other through `Rc`, the strong counter never drops to zero; no
  record appears in the ledger — the course's only leak.
- A weak reference (`Weak<T>`) points at the same value but never raises the strong
  counter; keeping one direction weak is enough to break the reference cycle.
- Once the reference cycle is broken, both values are dropped when the scope closes and the
  ledger produces two records at once.
- The compiler never rejects this reference cycle; the `Rc::clone` and `RefCell` writing
  goes through the exact same compilation path as non-reciprocal use.
- In the Go curriculum's Concurrency course, what leaked was a running unit; here what leaks
  is a value — neither comes to an end on its own without an external signal.

#### Course Wrap-Up

Across fourteen lessons, this course asked a single question: where does a value's life
end? The answer stayed readable from the source throughout — at scope end, where it was
moved, at an explicit call, together with its container, or, as seen in this lesson, never.
The course's first topic, Ownership Rules, built these classes and the borrow checker that
guards them; its second topic, Lifetimes, gave that check a name (lifetime) and said when
it must be written; its third topic, Shared Ownership, carried ownership past singularity
into being countable, shareable, and finally modifiable — but at every step the end point
was still deterministic, until this lesson showed two values holding onto each other.

Across the course's thirteen lessons, one concept passed by name only and was left
undefined: the `Drop` trait. Closures and iterators never appeared in this course at all;
all three share the same question — what a value **can do**, a question separate from how
long it lives — and the next course opens exactly that question.

