Methods & impl Blocks
Prerequisites: Structs
Methods are functions attached to a type. You define them in an
impl (implementation) block for that type.
Syntax
impl Rectangle {
fn new(width: f64, height: f64) -> Self { // associated function
Self { width, height }
}
fn area(&self) -> f64 { // reads the instance
self.width * self.height
}
fn scale(&mut self, factor: f64) { // modifies the instance
self.width *= factor;
self.height *= factor;
}
}
- The first parameter decides how the method uses the instance:
&self— borrows it immutably (most common: reading).&mut self— borrows it mutably (changing fields). The instance must be declaredmut.self— takes ownership; the instance can't be used afterwards. Useful for methods that transform a value into something else.
Selfis a shorthand for the type theimplblock is for.- Functions in an
implblock without aselfparameter are associated functions. They're called with::, likeRectangle::new(3.0, 4.0)orString::from("hi"). By convention a constructor is callednew. - Methods are called with a dot:
rect.area(). Rust automatically adds the&or&mutfor you, so you don't write(&rect).area().
Example
#[derive(Debug)]
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn new(width: f64, height: f64) -> Self {
Self { width, height }
}
fn square(size: f64) -> Self {
Self::new(size, size)
}
fn area(&self) -> f64 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
fn describe(self) -> String {
format!("{} x {} rectangle", self.width, self.height)
}
}
fn main() {
let mut rect = Rectangle::new(3.0, 4.0);
let small = Rectangle::square(2.0);
println!("area: {}", rect.area());
println!("can hold small? {}", rect.can_hold(&small));
rect.scale(2.0);
println!("after scaling: {rect:?}");
println!("new area: {}", rect.area());
let text = rect.describe(); // `rect` is moved into `describe`
println!("{text}");
}
Expected output
area: 12
can hold small? true
after scaling: Rectangle { width: 6.0, height: 8.0 }
new area: 48
6 x 8 rectangle
Notice that Debug prints floats as 6.0, while Display ({})
prints 6.
Common mistakes
- Calling a
&mut selfmethod on an immutable variable:
let counter = Counter { count: 0 };
counter.increment(); // increment takes &mut self
error[E0596]: cannot borrow `counter` as mutable, as it is not declared as mutable
Declare it with let mut counter.
- Calling an associated function with a dot, like
rect.new(1.0, 2.0). Functions withoutselfare called on the type:Rectangle::new(1.0, 2.0). - Forgetting
self.inside a method. Fields are only reachable throughself—widthon its own is an unknown variable. - Using
selfwhen you meant&self. A method takingselfconsumes the value, so the caller can't use it afterwards.
Practice
Create a Counter struct with a count: u32 field and an impl
block with:
new()— returns a counter starting at 0,increment(&mut self)— adds 1,value(&self) -> u32— returns the current count.
Increment a counter three times and print Count: 3.
Show solution
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Self { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
fn value(&self) -> u32 {
self.count
}
}
fn main() {
let mut counter = Counter::new();
counter.increment();
counter.increment();
counter.increment();
println!("Count: {}", counter.value());
}