Step 2 of 7
&mut self: methods that change the value
A method that changes the value takes &mut self: a mutable borrow of the value it's called on. Everything you know about &mut applies: you can change fields through it, and while the method runs nothing else can use the value.
struct Fighter {
name: String,
hp: u32,
}
impl Fighter {
fn take_hit(&mut self, damage: u32) {
self.hp = self.hp.saturating_sub(damage);
}
fn is_down(&self) -> bool {
self.hp == 0
}
}
fn main() {
let mut crab = Fighter { name: String::from("Ferris"), hp: 30 };
crab.take_hit(12);
println!("{} has {} hp", crab.name, crab.hp);
crab.take_hit(25);
println!("{} has {} hp, down: {}", crab.name, crab.hp, crab.is_down());
}
Ferris has 18 hp
Ferris has 0 hp, down: true
crab.take_hit(12)borrowscrabmutably for the call. Rust adds the&mutfor you, just like it adds&for&selfmethods.- Because the method needs a mutable borrow, the variable must be declared
let mut crab. is_downonly reads, so it takes&self. Pick the weakest receiver that does the job: readers take&self, so they can be called on any value, mutable or not.
Returning whether it worked
Some changes are not always allowed: you can't sell more than you have, or withdraw more than your balance. A simple way to say so is to return a bool and leave the value untouched when the answer is no:
struct Stock {
quantity: u32,
}
impl Stock {
fn sell(&mut self, amount: u32) -> bool {
if amount > self.quantity {
return false;
}
self.quantity -= amount;
true
}
}
fn main() {
let mut tea = Stock { quantity: 5 };
println!("sell 3: {}", tea.sell(3));
println!("sell 3: {}", tea.sell(3));
println!("left: {}", tea.quantity);
}
sell 3: true
sell 3: false
left: 2
The caller decides what to do with a false: print a message, try a smaller amount, and so on. Later you'll return richer answers with enums (the next module) and Result (the errors module).
Common mistakes
- Calling a
&mut selfmethod on a variable that isn'tmut:
struct Account {
balance: u32,
}
impl Account {
fn deposit(&mut self, amount: u32) {
self.balance += amount;
}
}
fn main() {
let acct = Account { balance: 0 };
acct.deposit(50);
println!("{}", acct.balance);
}
error[E0596]: cannot borrow `acct` as mutable, as it is not declared as mutable
- Changing a field in a
&selfmethod:error[E0594]: cannot assign to `self.count`, which is behind a `&` reference. The compiler's help suggests&mut self, which is right if the method really should change the value.
Your task
Finish the bank account. deposit adds amount to the balance. withdraw takes amount out only if the balance is big enough and returns true; otherwise it changes nothing and returns false. Every successful deposit or withdrawal adds 1 to transactions.
Challenge 2
Add fn transfer(&mut self, to: &mut Account, amount: u64) -> bool. It moves amount from self into to if self has enough, and returns whether it did. Reuse withdraw and deposit: call them on self and on to.
Challenge 3
Write an impl block for a Thermostat from scratch:
fn set_target(&mut self, target: i32): sets the target, but keeps it between 10 and 30 degrees (a target below 10 becomes 10, above 30 becomes 30).fn tick(&mut self): movescurrentone degree towardtarget(up if it's below, down if it's above, unchanged if equal).fn is_stable(&self) -> bool: true whencurrentequalstarget.
Previous: impl blocks and your first method Next: Associated functions and Self: constructors