Methods & Associated Functions
Methods are functions that are defined within the context of a
struct
(or enum or trait object).
Their first parameter is always self, which represents the instance
of the struct the method is
being called on. Methods let you organize functionality that belongs to a type.
Defining Methods
Methods are defined within an impl (implementation) block:
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
&self is short for
self: &Self. Within an
impl block, Self is an alias for the type that the
impl block is for.
Click Run to execute your code
Methods vs Associated Functions
The self Parameter
Methods can take ownership of self, borrow it immutably, or borrow
it mutably:
1. &self (Immutable Borrow)
Most common. Read-only access to the instance:
fn area(&self) -> u32 {
self.width * self.height
}
2. &mut self (Mutable Borrow)
When you need to modify the instance:
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
3. self (Take Ownership)
Rare. Consumes the instance:
fn into_string(self) -> String {
format!("{}x{}", self.width, self.height)
}
Associated Functions
Functions within impl blocks that don't take self are
called associated
functions. They're often used as constructors:
Click Run to execute your code
new, but this isn't enforced by the language.
Multiple impl Blocks
You can have multiple impl blocks for the same type:
Click Run to execute your code
Automatic Referencing and Dereferencing
Rust automatically adds &, &mut, or * to
match the method signature:
let rect = Rectangle { width: 30, height: 50 };
// These are equivalent
rect.area();
(&rect).area();
object.method(), Rust
automatically adds &, &mut, or * so
object matches the
signature of the method.
Method Call Syntax
| Type | Syntax | Example |
|---|---|---|
| Method | instance.method() |
rect.area() |
| Associated Function | Type::function() |
Rectangle::new(30, 50) |
Best Practices
- Use &self by default: Only use
&mut selforselfwhen needed - Create constructors: Use
new()as an associated function - Group related functionality: Put all methods for a type
in
implblocks - Use descriptive names: Method names should clearly indicate what they do
- Return Self for chaining: Return
&mut Selfto enable method chaining
self (taking ownership), the instance is consumed and can no longer be used. Only use this when you're transforming the instance into something else, like converting it to a different type.
Common Mistakes
1. Forgetting to use &self, &mut self, or self
// Wrong - Missing self parameter
impl Rectangle {
fn area() -> u32 { // Error: no self parameter
self.width * self.height
}
}
// Correct
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
2. Using &mut self when &self would suffice
// Wrong - Unnecessary mutability
impl Rectangle {
fn area(&mut self) -> u32 { // Doesn't need to mutate
self.width * self.height
}
}
// Correct - Use &self for read-only operations
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
3. Confusing methods with associated functions
// Wrong - Trying to call associated function like a method
let rect = Rectangle::new(30, 50);
let area = rect.new(30, 50); // Error: new doesn't have self
// Correct - Use :: for associated functions
let rect = Rectangle::new(30, 50);
let area = rect.area(); // Use . for methods
Exercise: Methods Practice
Task: Implement methods for BankAccount and Temperature structs.
Click Run to execute your code
Show Solution
impl BankAccount {
fn new(owner: String, initial_balance: f64) -> BankAccount {
BankAccount {
balance: initial_balance,
owner,
}
}
fn deposit(&mut self, amount: f64) {
self.balance += amount;
}
fn withdraw(&mut self, amount: f64) -> bool {
if self.balance >= amount {
self.balance -= amount;
true
} else {
false
}
}
fn balance(&self) -> f64 {
self.balance
}
}
impl Temperature {
fn from_celsius(celsius: f64) -> Temperature {
Temperature { celsius }
}
fn from_fahrenheit(fahrenheit: f64) -> Temperature {
Temperature {
celsius: (fahrenheit - 32.0) * 5.0 / 9.0,
}
}
fn to_fahrenheit(&self) -> f64 {
self.celsius * 9.0 / 5.0 + 32.0
}
fn is_freezing(&self) -> bool {
self.celsius <= 0.0
}
}
Summary
- Methods are functions defined in
implblocks - Methods have
selfas first parameter - &self: Immutable borrow (most common)
- &mut self: Mutable borrow (for modifications)
- self: Takes ownership (rare)
- Associated functions don't have
self - Call methods with
instance.method() - Call associated functions with
Type::function() - Rust automatically handles referencing/dereferencing
- Multiple
implblocks are allowed
What's Next?
Now that you understand structs and methods, you'll learn about enums - another way to create custom types. Enums let you define a type by enumerating its possible variants, and they're incredibly powerful when combined with pattern matching.
Enjoying these tutorials?