Step 3 of 7
Changing values through &mut with *
Method calls work through a &mut automatically. But operators and assignment that act on the value itself need the dereference operator *: *r means "the value r points to". Without it, you'd be doing arithmetic on the reference:
fn add_one(count: &mut i32) {
count += 1;
}
fn main() {
let mut c = 0;
add_one(&mut c);
println!("{}", c);
}
error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut i32`
--> src/main.rs:2:5
|
2 | count += 1;
| -----^^^^^
| |
| cannot use `+=` on type `&mut i32`
|
help: `+=` can be used on `i32` if you dereference the left-hand side
|
2 | *count += 1;
| +
With * it works:
fn add_points(score: &mut u32, points: u32) {
*score += points;
}
fn reset(name: &mut String) {
*name = String::from("(empty slot)"); // the old String is dropped here
}
fn main() {
let mut score = 100;
add_points(&mut score, 25);
add_points(&mut score, 5);
println!("score: {}", score);
let mut slot = String::from("Ferris");
reset(&mut slot);
println!("slot: {}", slot);
}
score: 130
slot: (empty slot)
*name = String::from(...) replaces the whole value behind the reference. Ownership still works: the old String ("Ferris") is dropped right there, and the caller's variable now owns the new one.
When do you need *?
| You write | Need *? |
|---|---|
r.push_str("x"), r.len() (method calls) |
No: automatic |
println!("{}", r) |
No |
*r += 1, *r = 0, *r * 2 (operators and assignment on the value) |
Yes |
if *r > 10 (comparing with a plain number) |
Yes |
You can also read a Copy value out through a reference: let copy = *r;.
Changing every element: for x in &mut v
for x in &mut v borrows the vector mutably and gives you a &mut to each element in turn, so you change elements with *x:
fn main() {
let mut prices = vec![100, 250, 40];
for price in &mut prices {
*price = *price * 9 / 10; // 10% off
}
println!("{:?}", prices);
}
[90, 225, 36]
If prices arrives in a function as a &mut Vec<u32> parameter, it's already a mutable reference: loop with for price in prices.
The three ways to loop over a vector:
| Loop | Each x is |
The vector afterwards |
|---|---|---|
for x in v |
the element itself (moved out) | gone |
for x in &v |
&T, read-only |
unchanged, still usable |
for x in &mut v |
&mut T, change it with *x |
changed, still usable |
Common mistakes
- Forgetting the
*.count += 1gives E0368;r = 5giveserror[E0308]: mismatched types(expected &mut i32, found integer), and the help suggests the*. - Adding
*to method calls.(*name).push_str("x")works but nobody writes it:name.push_str("x")does the same. - Assigning through a
&:error[E0594]: cannot assign to `*score`, which is behind a `&` reference. The parameter must be&mut.
Your task
A small bank account. Write deposit, which adds amount to the balance it borrows, and withdraw, which takes amount out only if the balance is at least amount. withdraw returns true if it took the money and false (leaving the balance unchanged) if there wasn't enough.
Challenge 2
heal should add amount to the hit points but never go above max. It's missing every *: fix the function (read the compiler's help lines) so the program prints:
hp: 60
hp: 100
Challenge 3
Write clamp_all. It borrows a vector of sensor readings mutably and changes every reading below low to low and every reading above high to high. Readings in between stay as they are.
For example, readings [-5, 12, 40] with low 0 and high 30 become [0, 12, 30].
Previous: Mutable references Next: The rules: many readers or one writer