---
title: Borrowing
source: 'https://academia.sh/en/courses/ownership/borrowing'
course: 'Ownership and the Memory Model'
language: en
updated: '2026-09-15T04:58:41+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Shows by running it that a borrowed value never ends anywhere while the borrow lasts, and is still accessible in its owner once the borrow's scope closes"
    - 'Distinguishes the difference between a shared and a mutable reference with examples'
    - "Proves with nested scopes that a reference's scope has to stay inside its source's scope"
---

# 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.
