Step 1 of 7
impl blocks and your first method
In the structs module you wrote functions like fn total_value(items: &[Item]). They work, but they float free: nothing ties them to the type, and you have to know their names to find them. A method is a function that belongs to a type. You define it inside an impl block (short for implementation), and its first parameter is self: the value the method was called on.
struct Rect {
width: u32,
height: u32,
}
impl Rect {
fn area(&self) -> u32 {
self.width * self.height
}
fn is_square(&self) -> bool {
self.width == self.height
}
}
fn main() {
let r = Rect { width: 8, height: 5 };
println!("area: {}", r.area());
println!("square? {}", r.is_square());
}
area: 40
square? false
Piece by piece:
impl Rect { ... }holds the functions that belong toRect. You can put as many methods in it as you like.&selfis short forself: &Self, and inside an impl blockSelfmeans "the type this impl is for", hereRect. Soareaborrows the rectangle, reads it, and gives it back. It's the same&Rectparameter you'd write for a free function, with a special name.self.widthreads a field of the value the method was called on.- You call a method with a dot:
r.area(). Rust passes&rasselffor you (automatic referencing), so you never write(&r).area().
In fact r.area() is just a nicer way to write Rect::area(&r), and both compile to exactly the same code. Methods are about organization, not speed:
- Discoverability: type
r.in an editor with rust-analyzer and you get a list of everything aRectcan do. - Behavior lives next to its data: one place to look when you change the struct.
- Shorter names:
r.area()instead ofrect_area(&r). Different types can each have their ownareamethod.
Common mistakes
- Forgetting
self: a function in an impl without aselfparameter has no value to read from:
struct Rect {
width: u32,
height: u32,
}
impl Rect {
fn area() -> u32 {
self.width * self.height
}
}
fn main() {
let r = Rect { width: 2, height: 3 };
println!("{}", r.area());
}
error[E0424]: cannot find value `self` in this scope
- Forgetting the parentheses:
r.area(without()) giveserror[E0615]: attempted to take value of method `area` on type `Rect`. Fields are read without parentheses, methods are called with them.
Your task
Fill in the blanks so the program prints perimeter: 28: the keyword that starts the block, the parameter that borrows the square, the value whose side you read, and the method call.
Challenge 2
Add two methods to Rect:
fn perimeter(&self) -> u32: twice the width plus twice the height.fn can_hold(&self, other: &Rect) -> bool: true ifselfis strictly wider and strictly taller thanother.
A method can take more parameters after self, like any function.
Challenge 3
Write an impl block for Time (hours and minutes on a 24-hour clock) with two methods:
fn total_minutes(&self) -> u32: minutes since midnight (9:30is 570).fn is_before(&self, other: &Time) -> bool: true ifselfis earlier in the day thanother. Calltotal_minutesfrom inside it withself.total_minutes().