Skip to main content

Mojo operator reference

Precedence table

Operator precedence and associativity. Higher-precedence operators bind tighter. Unless noted, operators associate left to right.

From highest to lowest precedence

PrecedenceOperatorsNotes
1() [] .Call, subscript, attribute
2**Exponentiation, right-associative
3+x -x ~xUnary prefix
4* @ / // %Multiplicative
5+ -Additive (addition, subtraction)
6<< >>Bitwise shift (left, right)
7&Bitwise AND
8^Bitwise XOR (not transfers)
9|Bitwise OR
10== != < <= > >=Comparisons, chainable
10in not inMembership, chainable
10is is notIdentity, chainable
11notBoolean NOT, prefix
12andBoolean AND, short-circuits
13orBoolean OR, short-circuits
14if-elseTernary, right-associative
15:=Walrus operator

Prefix operators: positive (+x), negative (-x), bitwise NOT complement (~x)

Multiplicative operators: times (*), matrix multiplication (@), divide (/, integer types round towards zero), truncating divide (//, integer types round towards negative infinity), modulo (%).

Comparison operators: equality (==), inequality (!=), less-than (<), less-than-or-equal (<=), greater-than (>), greater-than-or-equal (>=)

Right-associative operators

Most operators are left-associative. For example, a - b + c groups as (a - b) + c. Two infix operators are right-associative: exponentiation (a ** b) and Mojo's ternary if-else.

Exponentiation (**)

2 ** 3 ** 4

groups as

2 ** (3 ** 4)

Equivalent to pow(2, 81). In Mojo pow(a, b) and a ** b are interchangeable.

Ternary (if-else)

"low" if value < 10 else "high" if value > 100 else "mid"

groups as

"low" if value < 10 else ("high" if value > 100 else "mid")

Chaining operations

All comparison operators can be chained:

a < b < c        # equivalent to: (a < b) and (b < c)
a == b == c      # equivalent to: (a == b) and (b == c)
a < b == c       # equivalent to: (a < b) and (b == c)
a < b <= c != d  # equivalent to: (a < b) and (b <= c) and (c != d)

Each intermediate value is evaluated once.

  • Chaining only applies between operators at the same precedence. 2 ** 3 == 8 isn't a chain. It evaluates as (2 ** 3) == 8 since exponentiation binds tighter than comparison.

  • Comparison, membership, and identity operators share the same precedence and chain together. 5 != a < b in c is valid and evaluates as (5 != a) and (a < b) and (b in c).

Implementing operators for custom types

Mojo doesn't limit operators use to built-in types. Each operator has a set of dunder methods your custom types can implement. Once added, you can use operators in code instead of calling methods.

Infix operator method types

Each infix operator has up to three forms that decide which operand's method will run. For a op b:

  • Forward: Mojo tries the forward method first. a + b calls a.__add__(b).
  • Reversed: If the forward method doesn't exist or can't handle b's type, Mojo falls back to the reversed method on b. a + b calls b.__radd__(a).
  • In-place: Called for compound assignment. a += b calls a.__iadd__(b).

For example, if a uses a CustomVector type:

  • a + 5: calls the forward method a.__add__(5)
  • 5 + a: Int doesn't know custom types. Falls back to the reversed method, a.__radd__(5)
  • a += 5: calls the in-place a.__iadd__(5) method

Arithmetic

Implement the methods directly on your struct to use instance OP instance, instance OP= instance.

OperatorForwardReversedIn-place
+__add__()__radd__()__iadd__()
-__sub__()__rsub__()__isub__()
*__mul__()__rmul__()__imul__()
/__truediv____rtruediv__()__itruediv__()
//__floordiv____rfloordiv__()__ifloordiv__()
%__mod__()__rmod__()__imod__()
**__pow__()__rpow__()__ipow__()
@__matmul__()__rmatmul__()__imatmul__()

__pow__() and __ipow__() accept an optional third argument (modulus).

In-place operators are syntactic sugar for the operator applied to the variable with assignment:

x += y     # x = x + y
x -= y     # x = x - y
x *= y     # x = x * y
x /= y     # x = x / y
x //= y    # x = x // y
x %= y     # x = x % y
x **= y    # x = x ** y
x @= y     # x = x @ y

Traits:

Powable requires __pow__(), doesn't provide a default.

Bitwise

Implement the methods directly on your struct to use a OP b, a OP= b.

OperatorForwardReversedIn-place
&__and__()__rand__()__iand__()
|__or__()__ror__()__ior__()
^__xor__()__rxor__()__ixor__()
<<__lshift__()__rlshift__()__ilshift__()
>>__rshift__()__rrshift__()__irshift__()

Bitwise operators are typically implemented on integer and flag types. Like arithmetic operators, in-place bitwise operators are syntactic sugar for the operator applied to the variable with assignment:

x &= y     # x = x & y
x |= y     # x = x | y
x ^= y     # x = x ^ y
x <<= y    # x = x << y
x >>= y    # x = x >> y

Unary operators

Implement the methods directly on your struct to support prefix operators like -x, +x, and ~x.

OperatorMethod
-x__neg__()
+x__pos__()
~x__invert__()

Comparison operators

Implement the methods directly on your struct to use a OP b.

OperatorMethodTraitDefault?
==__eq__()EquatableYes
!=__ne__()EquatableYes
<__lt__()ComparableNo
<=__le__()ComparableYes
>__gt__()ComparableYes
>=__ge__()ComparableYes

Traits:

Equatable provides __eq__() if all your struct's fields conform to Equatable using pairwise field comparison. __ne__() derives from __eq__().

Comparable provides __le__(), __gt__(), and __ge__(), all derived from __lt__() and __eq__(). You implement __lt__().

Comparable refines Equatable, so conforming to Comparable requires both traits. If all your fields are Equatable, you implement __lt__() at a minimum.

Identity and membership operators

Implement the methods directly on your struct to use a OP b.

OperatorMethodTraitDefault?
is__is__()IdentifiableNo
is not__isnot__()IdentifiableYes
in__contains__()No
not in__contains__()No

x in collection calls collection.__contains__(x). The method is on the container, not the element. not in calls the same method and negates the result.

is tests object identity, not equality. Stdlib types that implement it include ArcPointer, PythonObject, and Optional (for is None checks).

Traits:

Identifiable requires __is__(). __isnot__() is provided (calls not (self is rhs)).

Subscript operators

Implement the methods directly on your struct to use a[key] reads and a[key] = b assigns.

OperationMethod
obj[key] (read)__getitem__()
obj[key] = val (write)__setitem__()

Both accept variadic arguments (for multi-dimensional indexing).

Was this page helpful?