Basic Methods
Structs are great for holding data, but you'll often want to define behavior on them too. That's where methods come in. You write methods inside an impl block, and the first parameter is always some version of self.
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
fn main() {
let rect = Rectangle {
width: 10.0,
height: 5.0,
};
println!("Area: {}", rect.area());
}
Try it Yourself ->
self, &self, and &mut self
You can choose how methods take ownership of self. Use &self for read-only access, &mut self for modifications, or just self when you want the method to consume the instance entirely. Most of the time, you'll use &self or &mut self.
struct Counter {
value: i32,
}
impl Counter {
fn increment(&mut self) {
self.value += 1;
}
fn reset(self) -> i32 {
self.value
}
}
fn main() {
let mut c = Counter { value: 0 };
c.increment();
println!("{}", c.value);
}
Try it Yourself ->
Associated Functions
Associated functions don't take self as a parameter, so they're tied to the type itself rather than an instance. The most common use is a constructor โ think ::new() on String or Vec.
struct Circle {
radius: f64,
}
impl Circle {
fn new(radius: f64) -> Circle {
Circle { radius }
}
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
}
fn main() {
let circle = Circle::new(5.0);
println!("Area: {:.2}", circle.area());
}
Try it Yourself ->
Multiple impl Blocks
Rust allows you to write multiple impl blocks for the same struct. This is especially useful when working with generics and traits, where different implementations apply to different constraints. For simple structs, you usually stick with one block.
struct Dog {
name: String,
}
impl Dog {
fn bark(&self) {
println!("{} says: Woof!", self.name);
}
}
impl Dog {
fn sit(&self) {
println!("{} sits.", self.name);
}
}
fn main() {
let dog = Dog { name: String::from("Rex") };
dog.bark();
dog.sit();
}
Try it Yourself ->