Rust Struct Methods
A struct stores data, and methods give that data behavior. Methods work like buttons on a remote control: the remote holds the state, and each button performs one action on it. Rust places methods inside impl blocks, which keep data and behavior close together.
Defining a Method
An impl block names the struct that receives the methods. The first parameter of a method is always self, which represents the instance.
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let room = Rectangle { width: 5, height: 4 };
println!("Area: {}", room.area());
}
Area: 20
The Three Forms of self
The way you write self decides how much control the method receives.
| Form | Meaning | Use When |
|---|---|---|
| &self | Read-only borrow | The method only looks at data |
| &mut self | Mutable borrow | The method changes data |
| self | Takes ownership | The method consumes the value |
Changing Data with &mut self
impl Rectangle {
fn grow(&mut self, amount: u32) {
self.width += amount;
self.height += amount;
}
}
fn main() {
let mut room = Rectangle { width: 5, height: 4 };
room.grow(2);
println!("{} x {}", room.width, room.height);
}
7 x 6
The variable must be declared with mut before a mutable method can run.
Consuming Methods
A method that takes self by value uses up the instance. The caller cannot touch the original afterward.
impl Rectangle {
fn into_square(self) -> u32 {
self.width.max(self.height)
}
}Associated Functions
A function inside impl without a self parameter is called an associated function. Call it with double colons. Developers use these as constructors, and the name new is a common convention.
impl Rectangle {
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
fn square(size: u32) -> Self {
Self { width: size, height: size }
}
}
fn main() {
let a = Rectangle::new(3, 6);
let b = Rectangle::square(4);
println!("{} {}", a.area(), b.area());
}The word Self with a capital S stands for the struct type itself. Renaming the struct later requires fewer edits.
Method Call Flow
room.area()
|
v
Rust sees: Rectangle::area(&room)
|
v
Method reads room.width and room.height
|
v
Returns 20
Rust adds the borrow symbols automatically in a method call. This feature, called automatic referencing, keeps the call syntax clean.
Methods with Extra Parameters
Parameters follow self in the list.
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let big = Rectangle::new(10, 8);
let small = Rectangle::new(4, 3);
println!("{}", big.can_hold(&small));
}
true
Several impl Blocks
One struct may have many impl blocks. Developers split methods into groups, such as constructors in one block and calculations in another. The compiler merges them all.
Methods on Tuple Structs
Tuple structs accept methods as well. Access fields with self.0 and self.1.
struct Meters(f64);
impl Meters {
fn to_feet(&self) -> f64 {
self.0 * 3.28084
}
}Naming Habits
- Use
newfor the main constructor. - Name getters after the field, such as
width(). - Begin boolean methods with
is_orhas_. - Keep each method focused on one job.
Common Mistakes
Forgetting mut on the variable blocks methods that take &mut self. The compiler reports that the variable cannot be borrowed as mutable. Add mut to the declaration.
Calling an associated function with a dot fails. Write Rectangle::new(3, 4) with double colons, and reserve the dot for methods on an existing value.
Taking self by value when &self is enough forces callers to give up their data. Choose the lightest form that gets the job done.
Practice Ideas
- Create a
BankAccountstruct withdeposit,withdraw, andbalancemethods. - Add a constructor named
newthat starts the balance at zero. - Write a
perimetermethod for the rectangle and print both results.
Organizing Larger Programs
Group related behavior in one impl block per struct. A reader opening the file sees the data definition first and every action that touches it close by. Small structs with focused methods stay easier to test than one giant struct that does everything.
Key Points
- Methods live inside
implblocks and start with a self parameter. &selfreads,&mut selfchanges, andselfconsumes.- Associated functions use
::and often build new instances. Selfrefers to the struct type.
