← 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.
Syntax
rust
impl Name {
    fn read(&self) {}
    fn modify(&mut self) {}
    fn consume(self) {}
}

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
// Define a struct `Book` to group related fields together
struct Book {
    title: String,
    pages: u32,
}

// Implement methods on `Book`
impl Book {
    // Define the function `summary` taking `&self` and returning a String
    fn summary(&self) -> String {
        format!("{} ({} pages)", self.title, self.pages)
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `book`, set to `Book { title: String::from("The Rust Book"), pages: 500 }`
    let book = Book { title: String::from("The Rust Book"), pages: 500 };
    // Print "{}" to the console, substituting in `book.summary()` (with a trailing newline)
    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
// Define a struct `Counter` to group related fields together
struct Counter {
    value: i32,
}

// Implement methods on `Counter`
impl Counter {
    // Define the function `increment` taking `self`
    fn increment(&mut self) {
        self.value += 1;
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare a mutable variable `counter`, initialized to `Counter { value: 0 }`
    let mut counter = Counter { value: 0 };
    counter.increment();
    counter.increment();
    // Print "Counter: {}" to the console, substituting in `counter.value` (with a trailing newline)
    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
// Define a struct `Wrapper` to group related fields together
struct Wrapper {
    value: String,
}

// Implement methods on `Wrapper`
impl Wrapper {
    // Define the function `unwrap` taking `self` and returning a String
    fn unwrap(self) -> String {
        self.value
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `wrapped`, set to `Wrapper { value: String::from("inside") }`
    let wrapped = Wrapper { value: String::from("inside") };
    // Declare an immutable variable `inner`, set to `wrapped.unwrap()`
    let inner = wrapped.unwrap();
    // Print "{}" to the console, substituting in `inner` (with a trailing newline)
    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
// Define a struct `Temperature` to group related fields together
struct Temperature {
    celsius: f64,
}

// Implement methods on `Temperature`
impl Temperature {
    // Define the function `to_fahrenheit` taking `&self` and returning a floating-point number
    fn to_fahrenheit(&self) -> f64 {
        self.celsius * 9.0 / 5.0 + 32.0
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `temp`, set to `Temperature { celsius: 20.0 }`
    let temp = Temperature { celsius: 20.0 };
    // Print "{}F" to the console, substituting in `temp.to_fahrenheit()` (with a trailing newline)
    println!("{}F", temp.to_fahrenheit());
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
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.