Start learning free

LESSON 51 OF 414 · FUNCTIONS I

Borrowing and References in Rust: & and &mut

Lesson Read the full explanation

In the previous lesson we saw that assigning a String to another variable moves it: the old owner dies. But most of the time we don't want to give a value away — we just want to look at it, or briefly edit it, and let the owner keep it. That is what references are for.

Creating a reference is called borrowing, and there are exactly two kinds:

  • &value — a shared (immutable) reference: read-only access, no ownership taken
  • &mut value — a mutable reference: read and write access, no ownership taken

Reading through &:

let city = String::from("Oslo");
let peek = &city;              // borrow — city is still the owner
println!("{}", peek);          // prints: Oslo
println!("{}", city);          // still works! Nothing moved.

Modifying through &mut:

let mut city = String::from("Oslo");
let editor = &mut city;        // the binding must be `mut` to allow this
editor.push_str(", Norway");
println!("{}", city);          // prints: Oslo, Norway

The borrowing rules (the part everyone trips on):

At any moment a value may have either many shared & references or exactly one &mut reference — never both, and never two &mut. Break the rule and the compiler stops you:

error[E0499]: cannot borrow `greeting` as mutable more than once at a time
 --> main.rs:4:13
  |
3 |     let a = &mut greeting;
  |             ------------- first mutable borrow occurs here
4 |     let b = &mut greeting;
  |             ^^^^^^^^^^^^^ second mutable borrow occurs here
5 |     a.push_str("!");
  |     - first borrow later used here

Mixing the two kinds while both are still in use fails the same way:

error[E0502]: cannot borrow `greeting` as mutable because it is also borrowed as immutable
 --> main.rs:4:18
  |
3 |     let reader = &greeting;
  |                  --------- immutable borrow occurs here
4 |     let writer = &mut greeting;
  |                  ^^^^^^^^^^^^^ mutable borrow occurs here
5 |     println!("{}", reader);
  |                    ------ immutable borrow later used here

The escape hatch: a borrow ends at its last use, not at the end of the block. If you finish using reader before creating writer, both fit in the same function and everything compiles.


Assignment
Follow the Coding Tutorial: create a mutable String named greeting holding "Hello", read it through a shared reference reader, then — after reader's job is done — append ", Rust!" through a mutable reference writer and print the result. Expected output:

reader sees = Hello
greeting = Hello, Rust!

Hint
Order matters: finish all reads through reader before you create writer, and the borrow checker will be happy.


STEP 1 OF 6
    Ctrl + Enter to check

    RUST · FUNCTIONS I

    Borrowing And References in Rust, explained

    What borrowing is

    Assigning a String to another variable moves it, and the old variable can't be used any more. Most of the time that is not what you want. You only want to look at a value, or change it briefly, and let the original owner keep it. A reference does that, and creating one is called borrowing. A borrow never takes ownership: the owner stays the owner, and the reference is a temporary way to reach the same value.

    Rust has two kinds of reference:

    • &value is a shared (immutable) reference. It gives read-only access.
    • &mut value is a mutable reference. It gives read and write access.

    Reading through &

    let city = String::from("Oslo");
    let peek = &city;
    println!("{}", peek);  // Oslo
    println!("{}", city);  // Oslo: nothing moved
    

    Both the reference and the owner can be read, because reading changes nothing. You can also have as many shared references to one value as you like at the same time.

    Writing through &mut

    let mut city = String::from("Oslo");
    let editor = &mut city;
    editor.push_str(", Norway");
    println!("{}", city);  // Oslo, Norway
    

    The owner must be declared with mut before you can take a &mut to it, because you can't hand out write access to something that is not writable. Method calls such as push_str work directly on the reference, so no * is needed. For plain values like integers you do write it: with let r = &mut n; the update is *r += 1;, and a variable n that started at 5 is then 6.

    The borrowing rules

    At any moment, a value can have either any number of & references or exactly one &mut reference. Never both, and never two &mut. The compiler enforces this, and breaking it is an error. Two mutable borrows fail like this:

    error[E0499]: cannot borrow `greeting` as mutable more than once at a time
      first mutable borrow occurs here
      second mutable borrow occurs here
      first borrow later used here

    Mixing a shared and a mutable borrow while both are still in use fails in the same way:

    error[E0502]: cannot borrow `greeting` as mutable because it is also borrowed as immutable
      immutable borrow occurs here
      mutable borrow occurs here
      immutable borrow later used here

    The rule exists for a good reason. If someone could change a value while someone else is reading it, the reader could see half-finished data. Rust prevents that at compile time instead of at run time.

    A borrow ends at its last use

    The phrase "while both are still in use" matters. A borrow does not last until the end of the block. It lasts until the reference's last use. If you finish with a shared reference before you create a mutable one, both can live in the same function, and the program compiles.

    In the error above, the last line used the old shared reference after the mutable one was created, so their lifetimes overlapped. Move that use earlier and the error disappears.

    The exercise, step by step

    The task is to read a string through a shared reference, then append to it through a mutable reference, and print both stages. The expected output is:

    reader sees = Hello
    greeting = Hello, Rust!
    
    1. Create the owner. greeting is a String::from("Hello"), declared with mut because a mutable borrow will be taken later. String::from gives an owned, growable string. A literal such as "Hello" is a borrowed &str and can't grow.
    2. Borrow it for reading. reader is &greeting, with no mut.
    3. Print through the reader. This is the reader's last use, so its borrow ends on this line.
    4. Borrow it for writing. writer is &mut greeting. It is allowed because no shared borrow is still alive.
    5. Append. push_str(", Rust!") on the writer changes the original string in place.
    6. Print the owner. greeting is usable again because the writer is not used after this point, so its mutable borrow is over.
    let mut greeting = String::from("Hello");
    let reader = &greeting;
    println!("reader sees = {}", reader);
    let writer = &mut greeting;
    writer.push_str(", Rust!");
    println!("greeting = {}", greeting);
    

    Order is the whole trick. If the first println! came after the line that creates writer, the shared borrow would still be alive, and the compiler would reject the program with the E0502 error shown earlier. Printing greeting on the last line is only legal because writer is never used again.

    Common mistakes

    • Forgetting mut on the owner. You can't take &mut of an immutable binding.
    • Keeping a shared reference around. Reading through reader after creating writer is the classic conflict.
    • Taking a second &mut while the first is still used later.
    • Using a value after moving it. Borrowing avoids this: let u = &t; leaves t usable, while let u = t; does not.

    Key takeaways

    • A reference borrows a value without taking ownership.
    • Many & or one &mut, never both at once.
    • A borrow ends at its last use, so ordering your reads and writes lets everything compile.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free