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.
