Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

Rust Methods and impl

A struct describes what something is. Methods describe what it can do. A rectangle knows its width and height; it would be lovely if it could also tell us its area. In Rust you add methods to a struct inside an impl block, short for implementation.

struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }

    fn is_square(&self) -> bool {
        self.width == self.height
    }
}

fn main() {
    let r = Rectangle { width: 6.0, height: 4.0 };
    println!("Area: {}", r.area());
    println!("Square? {}", r.is_square());
}
Output
Area: 24
Square? false

A method is a function whose first parameter is self, the instance it was called on. &self means the method borrows the instance to read it. Inside, you reach the fields with self.width. You call a method with a dot: r.area(), and Rust passes r in as self for you.

Methods that change the instance

When a method needs to change the instance, it takes &mut self, and the variable must be mut:

struct BankAccount {
    owner: String,
    balance: i64,
}

impl BankAccount {
    fn deposit(&mut self, amount: i64) {
        self.balance += amount;
    }

    fn withdraw(&mut self, amount: i64) -> bool {
        if amount > self.balance {
            return false;
        }
        self.balance -= amount;
        true
    }
}

fn main() {
    let mut account = BankAccount { owner: String::from("Ravi"), balance: 0 };
    account.deposit(500);
    println!("Withdraw 200: {}", account.withdraw(200));
    println!("Withdraw 900: {}", account.withdraw(900));
    println!("{} has {} rupees", account.owner, account.balance);
}
Output
Withdraw 200: true
Withdraw 900: false
Ravi has 300 rupees

Notice how the method protects the account: it refuses to take out more money than there is. Keeping rules like this inside methods means they are written in one place and cannot be forgotten somewhere else.

Associated functions and new

A function in an impl block that does not take self is called an associated function. It belongs to the type rather than to one instance, and you call it with two colons. The most common one is new, a friendly way to create an instance. You have already used one: String::new().

struct Timer {
    name: String,
    seconds: u32,
}

impl Timer {
    fn new(name: &str) -> Timer {
        Timer { name: name.to_string(), seconds: 0 }
    }

    fn tick(&mut self) {
        self.seconds += 1;
    }

    fn report(&self) -> String {
        format!("{} has run for {} s", self.name, self.seconds)
    }
}

fn main() {
    let mut t = Timer::new("Tea");
    for _ in 0..3 {
        t.tick();
    }
    println!("{}", t.report());
}
Output
Tea has run for 3 s

Inside an impl block you can also write Self instead of the type's name, as in fn new(name: &str) -> Self. Both mean the same thing.

Practice exercises

  1. Add a perimeter method to Rectangle and print it.
  2. Give BankAccount a new(owner: &str) associated function that starts at a balance of 0.
  3. Write a Circle struct with a radius, and methods area and scale(&mut self, factor: f64).