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
- Passing the value instead of a reference. If the function takes
&Stringand you passmsg, the types don't match:
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));
| +
- Trying to change a value through a
&reference. Plain references are read-only. The next step shows how to lend something for changing. - Taking ownership when you only need to read. If a function only looks at a value, its parameter should be a reference.
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().