Mojo Methods

A method is a function that belongs to a struct. It operates on the data stored in that struct. Methods keep behavior tied to the data it acts on, which makes code organized and easier to maintain. You have already seen methods in the Structs topic — this topic goes deeper into how they work and the different kinds available.

Method Categories

  Methods in a Mojo Struct
  ├── Instance methods    → operate on a specific instance (self)
  │   ├── Read-only       → fn method(self)
  │   └── Mutating        → fn method(inout self)
  └── Static methods      → belong to the struct type, not an instance

Instance Methods

An instance method always receives self as its first parameter. Every call to the method automatically passes the calling object as self.

struct Rectangle:
    var width: Float64
    var height: Float64

    fn __init__(inout self, w: Float64, h: Float64):
        self.width = w
        self.height = h

    fn area(self) -> Float64:
        return self.width * self.height

    fn perimeter(self) -> Float64:
        return 2.0 * (self.width + self.height)

    fn is_square(self) -> Bool:
        return self.width == self.height

fn main():
    var r = Rectangle(5.0, 3.0)
    print("Area:", r.area())          # 15.0
    print("Perimeter:", r.perimeter()) # 16.0
    print("Is square:", r.is_square()) # False

    var s = Rectangle(4.0, 4.0)
    print("Is square:", s.is_square()) # True

Mutating Methods

A method that changes any field of the struct declares inout self. This tells Mojo the method needs write access to the instance's data.

struct Counter:
    var count: Int

    fn __init__(inout self):
        self.count = 0

    fn increment(inout self):
        self.count += 1

    fn reset(inout self):
        self.count = 0

    fn value(self) -> Int:
        return self.count

fn main():
    var c = Counter()
    c.increment()
    c.increment()
    c.increment()
    print(c.value())   # 3
    c.reset()
    print(c.value())   # 0

Dunder Methods (Special Methods)

Dunder (double-underscore) methods are special method names that Mojo calls automatically in certain situations. You define them to control how your struct behaves with built-in operations.

__init__ — Construction

Runs when you create a new instance.

__del__ — Destruction

Runs automatically when an instance goes out of scope. Use it to release resources like file handles or allocated memory.

struct Resource:
    var name: String

    fn __init__(inout self, name: String):
        self.name = name
        print("Created:", self.name)

    fn __del__(owned self):
        print("Cleaned up:", self.name)

fn main():
    var r = Resource("DatabaseConnection")
    print("Using resource")
    # r goes out of scope here → __del__ runs automatically

Output:

Created: DatabaseConnection
Using resource
Cleaned up: DatabaseConnection

__str__ — String Representation

Returns a human-readable string when you convert the struct to text.

struct Vector2D:
    var x: Float64
    var y: Float64

    fn __init__(inout self, x: Float64, y: Float64):
        self.x = x
        self.y = y

    fn __str__(self) -> String:
        return "Vector2D(" + String(self.x) + ", " + String(self.y) + ")"

fn main():
    var v = Vector2D(3.0, 4.0)
    print(str(v))   # Vector2D(3.0, 4.0)

__len__ — Length

Called when you pass the struct to len().

struct WordList:
    var words: List[String]

    fn __init__(inout self):
        self.words = List[String]()

    fn add(inout self, word: String):
        self.words.append(word)

    fn __len__(self) -> Int:
        return len(self.words)

fn main():
    var wl = WordList()
    wl.add("mojo")
    wl.add("fast")
    wl.add("fun")
    print(len(wl))   # 3

Static Methods

A static method belongs to the struct type itself, not to any specific instance. It does not receive self. Call it using the struct name directly.

struct MathTools:
    @staticmethod
    fn square(x: Float64) -> Float64:
        return x * x

    @staticmethod
    fn cube(x: Float64) -> Float64:
        return x * x * x

fn main():
    print(MathTools.square(5.0))   # 25.0
    print(MathTools.cube(3.0))     # 27.0
Static vs Instance call:

  Instance:  my_rect.area()    → needs an object
  Static:    MathTools.square(5.0) → no object needed

Method Chaining

Methods that return self (or a new instance) can be chained together in one expression. This produces readable, pipeline-style code.

struct Builder:
    var parts: String

    fn __init__(inout self):
        self.parts = ""

    fn add(inout self, part: String) -> String:
        self.parts += part + " "
        return self.parts

fn main():
    var b = Builder()
    _ = b.add("Mojo")
    _ = b.add("is")
    _ = b.add("fast")
    print(b.parts)   # Mojo is fast

Key Takeaways

Instance methods receive self and operate on a specific object. Read-only methods use fn method(self); mutating methods use fn method(inout self). Dunder methods like __init__, __del__, __str__, and __len__ integrate your struct with Mojo's built-in operations. Static methods belong to the type rather than an instance and are called with the struct name. Designing your methods with clear responsibilities makes structs easy to use and maintain.

Leave a Comment

Your email address will not be published. Required fields are marked *