SyntaxStudy
Sign Up
Rust Methods and Associated Functions with impl
Rust Beginner 1 min read

Methods and Associated Functions with impl

Behaviour is added to a struct through `impl` blocks. Methods defined inside `impl` receive the instance as their first parameter, which can be `&self` (immutable borrow), `&mut self` (mutable borrow), or `self` (take ownership). The compiler automatically inserts the correct reference and dereference operations when you call a method, so `point.distance()` is equivalent to `Point::distance(&point)`. Associated functions (analogous to static methods in other languages) are defined inside `impl` but do not take `self`. They are called with the `::` syntax, e.g. `Point::new(1.0, 2.0)`. By convention, `new` is used as a constructor, but Rust has no built-in notion of constructors — `new` is just a name. You can have multiple `impl` blocks for the same type, which is useful when implementing traits. A struct can have multiple `impl` blocks, and additional `impl` blocks can appear in the same file or in a separate module. This allows library authors to separate core functionality from trait implementations. The combination of ownership rules and method dispatch makes Rust structs capable of expressing the same rich object-oriented patterns found in other languages while remaining explicit about memory management.
Example
#[derive(Debug)]
struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    // Associated function (constructor)
    pub fn new(width: f64, height: f64) -> Self {
        assert!(width > 0.0 && height > 0.0, "dimensions must be positive");
        Rectangle { width, height }
    }

    // Immutable method
    pub fn area(&self) -> f64 {
        self.width * self.height
    }

    pub fn perimeter(&self) -> f64 {
        2.0 * (self.width + self.height)
    }

    pub fn is_square(&self) -> bool {
        (self.width - self.height).abs() < f64::EPSILON
    }

    // Takes another Rectangle by reference
    pub fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }

    // Mutable method
    pub fn scale(&mut self, factor: f64) {
        self.width *= factor;
        self.height *= factor;
    }
}

fn main() {
    let mut r1 = Rectangle::new(10.0, 5.0);
    let r2 = Rectangle::new(3.0, 2.0);

    println!("r1 = {:?}", r1);
    println!("area = {}  perimeter = {}", r1.area(), r1.perimeter());
    println!("is square: {}  can hold r2: {}", r1.is_square(), r1.can_hold(&r2));

    r1.scale(2.0);
    println!("after scale(2): {:?}", r1);
}