RustArena

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

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

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

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:

Previous: impl blocks and your first method Next: Associated functions and Self: constructors