Implementing Methods in Rust
Attaching behavior to structs and enums with impl blocks, and the difference between methods and associated functions.
3 min read
Structs and enums hold data; impl blocks give them behavior. This is how Rust achieves what a class's methods do in other languages, without inheritance.
Defining methods
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
fn main() {
let rect = Rectangle { width: 30.0, height: 50.0 };
println!("Area: {}", rect.area());
}&self is the first parameter of every method — it's shorthand for self: &Self, a borrowed reference to the instance the method was called on. It follows the exact same borrowing rules from earlier in this course:
&self— borrows the instance immutably; use this when the method only reads data.&mut self— borrows it mutably; use this when the method needs to modify a field.self(no&) — takes ownership of the instance outright; rare, used mainly when a method deliberately consumes and transforms the value into something else.
impl Rectangle {
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
}
fn main() {
let mut rect = Rectangle { width: 30.0, height: 50.0 };
rect.scale(2.0);
println!("{}", rect.width); // 60.0
}Methods can take other parameters
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle { width: 30.0, height: 50.0 };
let rect2 = Rectangle { width: 10.0, height: 40.0 };
println!("{}", rect1.can_hold(&rect2)); // true
}Associated functions: no self
A function inside an impl block that doesn't take self is an associated function, not a method — called with :: instead of .. The most common use is a constructor:
impl Rectangle {
fn square(size: f64) -> Rectangle {
Rectangle { width: size, height: size }
}
}
fn main() {
let sq = Rectangle::square(20.0);
}String::from(...), which you've used throughout this course, is exactly this pattern — an associated function on String, not a method on an existing instance.
Multiple impl blocks
A type can have more than one impl block; Rust doesn't require everything in one place. This is occasionally useful for organizing large types, or (as you'll see in the traits lesson) for separating a type's own methods from the trait implementations it provides:
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
impl Rectangle {
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
}Enums get methods too
impl isn't limited to structs — enums can have methods in exactly the same way:
enum TrafficLight {
Red,
Yellow,
Green,
}
impl TrafficLight {
fn duration_secs(&self) -> u32 {
match self {
TrafficLight::Red => 30,
TrafficLight::Yellow => 5,
TrafficLight::Green => 25,
}
}
}Grouping behavior with the data it operates on — rather than as loose standalone functions — is the core organizational habit impl blocks encourage, and it's one you'll use constantly once you start building anything beyond a single-file example.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.