Mojo Operator Overloading
Operator overloading lets you define what operators like +, -, *, and == mean for your custom struct types. Instead of calling a method like v1.add(v2), you write v1 + v2, which reads naturally and makes your types behave like built-in types.
Why Overload Operators
Without overloading: With overloading: var c = a.add(b) var c = a + b var d = c.multiply(3.0) var d = c * 3.0 if a.equals(b): if a == b: Readable? Barely. Reads like math.
The Vector2D Example
struct Vector2D:
var x: Float64
var y: Float64
fn __init__(inout self, x: Float64, y: Float64):
self.x = x
self.y = y
fn __add__(self, other: Self) -> Self:
return Vector2D(self.x + other.x, self.y + other.y)
fn __sub__(self, other: Self) -> Self:
return Vector2D(self.x - other.x, self.y - other.y)
fn __mul__(self, scalar: Float64) -> Self:
return Vector2D(self.x * scalar, self.y * scalar)
fn __eq__(self, other: Self) -> Bool:
return self.x == other.x and self.y == other.y
fn __str__(self) -> String:
return "(" + String(self.x) + ", " + String(self.y) + ")"
fn main():
var v1 = Vector2D(3.0, 4.0)
var v2 = Vector2D(1.0, 2.0)
var sum = v1 + v2
var diff = v1 - v2
var scaled = v1 * 2.0
print(str(sum)) # (4.0, 6.0)
print(str(diff)) # (2.0, 2.0)
print(str(scaled)) # (6.0, 8.0)
print(v1 == v2) # False
Operator Dunder Method Map
Operator | Dunder Method | Example ----------|-----------------|---------------------------- + | __add__ | a + b - | __sub__ | a - b * | __mul__ | a * b / | __truediv__ | a / b // | __floordiv__ | a // b % | __mod__ | a % b ** | __pow__ | a ** b == | __eq__ | a == b != | __ne__ | a != b < | __lt__ | a < b <= | __le__ | a <= b > | __gt__ | a > b >= | __ge__ | a >= b -x | __neg__ | -a (unary minus) len() | __len__ | len(a) str() | __str__ | str(a) [] | __getitem__ | a[i] []= | __setitem__ | a[i] = v
Comparison Operators for Sorting
struct Score:
var player: String
var points: Int
fn __init__(inout self, player: String, points: Int):
self.player = player
self.points = points
fn __lt__(self, other: Self) -> Bool:
return self.points < other.points
fn __gt__(self, other: Self) -> Bool:
return self.points > other.points
fn __eq__(self, other: Self) -> Bool:
return self.points == other.points
fn main():
var alice = Score("Alice", 95)
var bob = Score("Bob", 80)
if alice > bob:
print(alice.player, "scored higher") # Alice scored higher
elif alice < bob:
print(bob.player, "scored higher")
else:
print("Tied")
Indexing with __getitem__ and __setitem__
struct Matrix2x2:
var data: SIMD[DType.float64, 4]
fn __init__(inout self, a: Float64, b: Float64, c: Float64, d: Float64):
self.data = SIMD[DType.float64, 4](a, b, c, d)
fn __getitem__(self, index: Int) -> Float64:
return self.data[index]
fn __setitem__(inout self, index: Int, value: Float64):
self.data[index] = value
fn main():
var m = Matrix2x2(1.0, 2.0, 3.0, 4.0)
print(m[0]) # 1.0
print(m[3]) # 4.0
m[1] = 99.0
print(m[1]) # 99.0
Unary Operators
struct Temperature:
var celsius: Float64
fn __init__(inout self, c: Float64):
self.celsius = c
fn __neg__(self) -> Self:
return Temperature(-self.celsius)
fn __str__(self) -> String:
return String(self.celsius) + "C"
fn main():
var t = Temperature(25.0)
var inv = -t
print(str(t)) # 25.0C
print(str(inv)) # -25.0C
In-Place Operators
In-place operators like += modify the left operand directly. Define __iadd__ and similar methods to support them.
struct Counter:
var value: Int
fn __init__(inout self, v: Int):
self.value = v
fn __iadd__(inout self, amount: Int):
self.value += amount
fn __isub__(inout self, amount: Int):
self.value -= amount
fn main():
var c = Counter(10)
c += 5
print(c.value) # 15
c -= 3
print(c.value) # 12
Key Takeaways
Operator overloading maps Mojo operators to dunder methods you define in your struct. The compiler calls __add__ when it sees a + b, __eq__ for a == b, and so on. Overloading makes custom types feel natural to use. Define only the operators your type logically supports — adding two file paths makes no sense, so do not define __add__ for a path struct. Comparison operators unlock sorting and ordering. Index operators __getitem__ and __setitem__ give your struct array-style access.
