Methods

Methods let you define functions that belong to a type, called with dot syntax. They keep related operations grouped with the data they operate on.

Defining Methods

Use impl blocks to add methods to a struct:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn distance_squared(self) -> i32 {
        self.x * self.x + self.y * self.y
    }
}

fn main() -> i32 {
    let p = Point { x: 3, y: 4 };
    let d = p.distance_squared();
    @dbg(d);  // prints: 25
    d
}

The first parameter self receives the struct value. Methods are called with dot syntax: p.distance_squared().

Methods with Parameters

Methods can take additional parameters beyond self:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn translate(self, dx: i32, dy: i32) -> Point {
        Point { x: self.x + dx, y: self.y + dy }
    }
}

fn main() -> i32 {
    let p = Point { x: 1, y: 2 };
    let q = p.translate(3, 4);

    @dbg(q.x);  // prints: 4
    @dbg(q.y);  // prints: 6

    q.x
}

Mutating Methods with inout self

To modify a struct through a method, use inout self. Unlike free functions (where the caller writes inout at the call site), method receivers are implicit — you just call the method normally on a mutable variable:

struct Counter {
    value: i32,
}

impl Counter {
    fn increment(inout self) {
        self.value = self.value + 1;
    }

    fn reset(inout self) {
        self.value = 0;
    }
}

fn main() -> i32 {
    let mut c = Counter { value: 0 };
    c.increment();
    c.increment();
    c.increment();

    @dbg(c.value);  // prints: 3

    c.reset();

    @dbg(c.value);  // prints: 0

    0
}

Read-Only Methods with borrow self

Use borrow self when a method only needs to read, not modify, the struct. This lets you call the method without consuming the value:

struct Rectangle {
    width: i32,
    height: i32,
}

impl Rectangle {
    fn area(borrow self) -> i32 {
        self.width * self.height
    }

    fn perimeter(borrow self) -> i32 {
        2 * (self.width + self.height)
    }
}

fn main() -> i32 {
    let r = Rectangle { width: 6, height: 4 };

    @dbg(r.area());       // prints: 24
    @dbg(r.perimeter());  // prints: 20

    // r is still usable because borrow self didn't consume it
    r.area()
}

Associated Functions

Functions inside impl blocks without a self parameter are associated functions. They're called with Type::function() syntax and are useful for constructors:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn origin() -> Point {
        Point { x: 0, y: 0 }
    }

    fn new(x: i32, y: i32) -> Point {
        Point { x: x, y: y }
    }
}

fn main() -> i32 {
    let origin = Point::origin();
    let p = Point::new(3, 4);

    @dbg(origin.x);  // prints: 0
    @dbg(p.x);       // prints: 3

    0
}

Chaining Methods

Methods that return the struct can be chained:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn translate(self, dx: i32, dy: i32) -> Point {
        Point { x: self.x + dx, y: self.y + dy }
    }

    fn scale(self, factor: i32) -> Point {
        Point { x: self.x * factor, y: self.y * factor }
    }
}

fn main() -> i32 {
    let p = Point::new(1, 2).translate(3, 4).scale(2);
    // (1+3)*2=8, (2+4)*2=12
    @dbg(p.x);  // prints: 8
    @dbg(p.y);  // prints: 12
    p.x
}

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x: x, y: y }
    }
}

Summary

ParameterSyntaxUse When
By valueselfMethod consumes or transforms the value
Mutableinout selfMethod modifies the struct in place
Read-onlyborrow selfMethod only reads from the struct

Methods keep related operations next to the data they work on, making code easier to discover and organize.