RustArena

Step 1 of 7

References: borrowing a value

The ownership module ended with an awkward pattern: to let a function look at a String, you had to move it in and get it back in a tuple. Real Rust code almost never does that. Instead, it lends the function a reference.

A reference is a pointer to a value that someone else owns. You make one with &: &msg means "a reference to msg". The type of a reference to a String is &String. Making a reference is called borrowing: you get to use the value, but you don't own it, so you never drop it, and the owner still has it afterwards.

fn char_count(text: &String) -> usize {   // borrows a String
    text.chars().count()
}                                          // text goes away, but it owned nothing: nothing is dropped

fn main() {
    let msg = String::from("good game");
    let n = char_count(&msg);              // lend msg to the function
    println!("'{}' has {} characters", msg, n);   // msg is still ours
}
'good game' has 9 characters

Compare it with the tuple version from the ownership module: no giving back, no shadowing. In memory, the reference is just one more pointer, pointing at the owner:

   char_count's frame          main's frame
   text                        msg
  ┌────────────┐              ┌────────────┐          HEAP
  │ ptr ───────┼────────────► │ ptr ───────┼─────► ┌───┬───┬───┬─ ─ ─┬───┐
  └────────────┘              │ len 9      │       │ g │ o │ o │ ... │ e │
                              │ cap 9      │       └───┴───┴───┴─ ─ ─┴───┘
                              └────────────┘

When char_count returns, text disappears. It was only a borrow, so the String isn't dropped: msg is still its owner.

Reading through a reference

Method calls and println! work through references automatically: text.chars(), text.len() and println!("{}", text) all reach the String behind the reference. You don't need any extra syntax to read through a &.

You can borrow in a let and in a for loop too. for x in &v loops over a vector without moving it: each x is a reference to one element.

fn main() {
    let names = vec![String::from("Ana"), String::from("Ben")];
    for name in &names {              // borrow the vector: name is a &String
        println!("hello, {}", name);
    }
    println!("{} names, still ours", names.len());
    let first = &names[0];            // borrow one element: no clone needed
    println!("first: {}", first);
}
hello, Ana
hello, Ben
2 names, still ours
first: Ana

Those are exactly the fixes the compiler suggested for the loop and index errors in the ownership module. With a Vec<i32> or Vec<f64>, each x in for x in &v is a &i32 or &f64, and arithmetic accepts those directly: sum += x works.

You've been using one kind of reference since your first program: string literals. Their type, &str, is a reference to text stored in the program itself. The slices module covers &str properly; for now, it's the type to write for a parameter that receives a string literal.

Common mistakes

fn char_count(text: &String) -> usize {
    text.chars().count()
}

fn main() {
    let msg = String::from("gg");
    println!("{}", char_count(msg));
}
error[E0308]: mismatched types
 --> src/main.rs:7:31
  |
7 |     println!("{}", char_count(msg));
  |                    ---------- ^^^ expected `&String`, found `String`
  |
help: consider borrowing here
  |
7 |     println!("{}", char_count(&msg));
  |                               +

Your task

Fill in the blanks so average borrows the vector of temperatures instead of taking ownership of it (the parameter type, then the argument), so main can still print the readings afterwards. The program should print:

average of [18.5, 21.0, 19.5, 22.0]: 20.25

Challenge 2

print_receipt takes ownership of the order, so the second call and the last line don't compile. Change the function to borrow the order instead, and update the calls. Don't use .clone(). The program should print:

Receipt for #204 noodles
Receipt for #204 noodles
Archived #204 noodles

Challenge 3

Write count_long. It borrows a vector of words and returns how many of them have at least min characters. Count characters with .chars().count(), not bytes, so "café" counts as 4. Don't use .clone().

Next: Mutable references