Operators in Mojo
Explore arithmetic, comparison, logical, and bitwise operators in Mojo with practical Docker-ready examples
Operators are the building blocks of every expression you write. In Mojo, operators look and behave almost exactly like their Python counterparts—+, -, *, /, //, %, **, ==, and, or, and not all work as you would expect. The difference is what happens under the hood: when your variables have concrete types, Mojo’s MLIR-based compiler lowers these operators to native machine instructions, with no interpreter overhead.
Because Mojo is a Python superset with optional static typing, you can use operators in a relaxed Python style inside def functions, or in a strict, type-checked style inside fn functions. The compiler picks the right instructions based on the operand types: integer addition becomes a hardware add, floating-point division becomes an FPU instruction, and so on.
This tutorial walks through the operator categories you’ll use most: arithmetic, comparison, logical, bitwise, and compound assignment. Each example is a small, runnable Mojo program you can build and execute through the Docker image.
Arithmetic Operators
Mojo supports the same set of arithmetic operators as Python, including floor division (//), modulo (%), and exponentiation (**).
Create a file named arithmetic.mojo:
def main():
var a: Int = 17
var b: Int = 5
print("a + b =", a + b)
print("a - b =", a - b)
print("a * b =", a * b)
print("a / b =", Float64(a) / Float64(b))
print("a // b =", a // b)
print("a % b =", a % b)
print("a ** b =", a ** b)
print("-a =", -a)
True division (/) produces a floating-point result, so the integer operands are converted explicitly to Float64. Floor division (//) keeps the result as an integer, and ** raises the left operand to the power of the right.
Comparison and Logical Operators
Comparison operators (==, !=, <, <=, >, >=) return Bool values. Mojo’s logical operators—and, or, not—short-circuit just like in Python.
Create a file named comparison.mojo:
def main():
var x: Int = 10
var y: Int = 20
print("x == y:", x == y)
print("x != y:", x != y)
print("x < y:", x < y)
print("x <= 10:", x <= 10)
print("x > y:", x > y)
print("x >= 10:", x >= 10)
var in_range: Bool = x > 0 and x < 100
var is_edge: Bool = x == 0 or y == 20
print("0 < x < 100:", in_range)
print("x == 0 or y == 20:", is_edge)
print("not in_range:", not in_range)
Mojo also supports Python-style chained comparisons. 0 < x < 100 is equivalent to 0 < x and x < 100 and evaluates x exactly once.
Bitwise Operators
Bitwise operators work on the binary representation of integers. They are useful for flag manipulation, low-level encoding, and—because Mojo compiles to native code—for squeezing maximum performance out of integer-heavy loops.
Create a file named bitwise.mojo:
def main():
var a: Int = 12 # 0b1100
var b: Int = 10 # 0b1010
print("a & b =", a & b) # AND -> 0b1000 = 8
print("a | b =", a | b) # OR -> 0b1110 = 14
print("a ^ b =", a ^ b) # XOR -> 0b0110 = 6
print("~a =", ~a) # NOT -> -13 (two's complement)
print("a << 2 =", a << 2) # left shift -> 48
print("a >> 1 =", a >> 1) # right shift -> 6
~a produces -(a + 1) in two’s complement, which is why ~12 is -13. Shifts by n positions are equivalent to multiplying or dividing by 2**n, but they map to single hardware instructions.
Compound Assignment and Precedence
Compound assignment operators (+=, -=, *=, /=, //=, %=, **=, plus the bitwise variants) update a variable in place. They require the variable to be declared with var so that it is mutable.
Create a file named assignment.mojo:
def main():
var total: Int = 0
total += 10
total += 5
total -= 3
total *= 2
print("total =", total)
# Operator precedence: ** binds tighter than * which binds tighter than +
var result: Int = 2 + 3 * 4 ** 2
print("2 + 3 * 4 ** 2 =", result)
# Parentheses change the grouping
var grouped: Int = (2 + 3) * 4 ** 2
print("(2 + 3) * 4 ** 2 =", grouped)
# Boolean short-circuit: the right side is never evaluated when unnecessary
var safe: Bool = total != 0 and (100 // total) > 0
print("safe check:", safe)
Mojo’s precedence rules follow Python’s: exponentiation binds tightest, followed by unary -, then *//////%, then +/-, then comparison, then not, and, or. When in doubt, parentheses make intent explicit and cost nothing at runtime.
Running with Docker
| |
Expected Output
Running arithmetic.mojo:
a + b = 22
a - b = 12
a * b = 85
a / b = 3.4
a // b = 3
a % b = 2
a ** b = 1419857
-a = -17
Running comparison.mojo:
x == y: False
x != y: True
x < y: True
x <= 10: True
x > y: False
x >= 10: True
0 < x < 100: True
x == 0 or y == 20: True
not in_range: False
Running bitwise.mojo:
a & b = 8
a | b = 14
a ^ b = 6
~a = -13
a << 2 = 48
a >> 1 = 6
Running assignment.mojo:
total = 24
2 + 3 * 4 ** 2 = 50
(2 + 3) * 4 ** 2 = 80
safe check: True
Key Concepts
- Python-familiar syntax —
+,-,*,/,//,%,**and the comparison/logical operators behave the same as in Python. - True vs floor division —
/always yields a floating-point result;//performs floor division and preserves integer types. - Compile-time lowering — When operand types are known, Mojo’s compiler emits direct machine instructions, so operators carry no interpreter overhead.
- Chained comparisons — Expressions like
0 < x < 100work natively and evaluate the middle term only once. - Short-circuit logic —
andandorstop evaluating as soon as the result is determined, which is useful for guarding against division by zero or null-like values. - Bitwise operators on
Int—&,|,^,~,<<, and>>map to hardware instructions, making them ideal for flags, masks, and low-level encoding. - Mutability is explicit — Compound assignment requires
vardeclarations; function arguments are immutable by default unless declaredinout. - Standard precedence — Mojo follows Python’s precedence rules. When mixing arithmetic, comparison, and logical operators, parentheses make intent obvious at no runtime cost.
Running Today
All examples can be run using Docker:
docker pull codearchaeology/mojo:latest
Comments
Loading comments...
Leave a Comment