← Back to Rust Course | Chapter 7: Structs | Lesson 3 of 7

Methods and self

Methods are actions a struct can do, and self is just how the method refers to the very struct it's attached to.

Reading Data with &self

A method that only needs to read the struct's fields takes &self, an immutable borrow of the instance. This is the most common kind of method.

Example: Reading Data with &self

markup
struct Book {
    title: String,
    pages: u32,
}

impl Book {
    fn summary(&self) -> String {
        format!("{} ({} pages)", self.title, self.pages)
    }
}

fn main() {
    let book = Book { title: String::from("The Rust Book"), pages: 500 };
    println!("{}", book.summary());
}

Modifying Data with &mut self

A method that changes one of the struct's fields must take &mut self, a mutable borrow, and the variable holding the instance must itself be declared mut.

Example: Modifying Data with &mut self

markup
struct Counter {
    value: i32,
}

impl Counter {
    fn increment(&mut self) {
        self.value += 1;
    }
}

fn main() {
    let mut counter = Counter { value: 0 };
    counter.increment();
    counter.increment();
    println!("Counter: {}", counter.value);
}

Consuming self

A method that takes plain self (no &) takes full ownership of the instance, consuming it. This is used when a method transforms a value into something else and the original should no longer be usable.

Example: Consuming self

markup
struct Wrapper {
    value: String,
}

impl Wrapper {
    fn unwrap(self) -> String {
        self.value
    }
}

fn main() {
    let wrapped = Wrapper { value: String::from("inside") };
    let inner = wrapped.unwrap();
    println!("{}", inner);
}

Dot Syntax Handles Referencing Automatically

When you call instance.method(), Rust automatically inserts the right amount of referencing or dereferencing to match what the method's self parameter expects, so you rarely need to write (&instance).method() manually.

Example: Dot Syntax Handles Referencing Automatically

markup
struct Temperature {
    celsius: f64,
}

impl Temperature {
    fn to_fahrenheit(&self) -> f64 {
        self.celsius * 9.0 / 5.0 + 32.0
    }
}

fn main() {
    let temp = Temperature { celsius: 20.0 };
    println!("{}F", temp.to_fahrenheit());
}
Common Mistakes
  1. Writing self when you meant &self, which would move the struct into the method instead of just borrowing it.
  2. Forgetting &mut self is required for any method that modifies one of the struct's own fields.
  3. Calling a method as Type::method(instance) when the simpler instance.method() dot-call syntax is clearer and idiomatic.
Chapter Summary
  • &self borrows the instance immutably, letting a method read its fields without taking ownership.
  • &mut self borrows the instance mutably, letting a method modify its fields.
  • Plain self (without &) takes ownership of the instance, consuming it -- used less often, typically for conversions.
  • Dot-call syntax instance.method() automatically handles referencing/dereferencing self for you.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.