Mojo Decorators
A decorator modifies the behavior of a function or struct without changing its code. You apply a decorator by placing its name prefixed with @ on the line immediately above the function or struct definition. Mojo provides several built-in decorators that unlock performance optimizations, compile-time evaluation, and static method behavior.
What a Decorator Does
Without decorator: With decorator:
fn my_func(): @always_inline
... fn my_func():
...
↑
Compiler instruction:
"Always inline this function's body
at every call site"
@always_inline
Forces the compiler to copy the function body directly into every location that calls it, eliminating function call overhead. Use this for small, frequently called functions.
@always_inline
fn clamp(value: Float32, lo: Float32, hi: Float32) -> Float32:
if value < lo:
return lo
if value > hi:
return hi
return value
fn main():
print(clamp(1.5, 0.0, 1.0)) # 1.0
print(clamp(0.5, 0.0, 1.0)) # 0.5
Without @always_inline: With @always_inline: main calls clamp → push args compiler pastes clamp body at call site: → jump to clamp code if value < lo: return lo → execute body if value > hi: return hi → return return value Overhead: ~5 ns per call Overhead: 0 ns (no call at all)
@staticmethod
Marks a method as belonging to the struct type rather than an instance. Static methods do not receive self and can be called without creating an object.
struct MathUtils:
@staticmethod
fn is_prime(n: Int) -> Bool:
if n < 2:
return False
for i in range(2, n):
if n % i == 0:
return False
return True
@staticmethod
fn gcd(a: Int, b: Int) -> Int:
var x = a
var y = b
while y != 0:
var temp = y
y = x % y
x = temp
return x
fn main():
print(MathUtils.is_prime(17)) # True
print(MathUtils.is_prime(18)) # False
print(MathUtils.gcd(48, 18)) # 6
@parameter
Marks a nested function or if-statement for compile-time evaluation. The compiler resolves @parameter blocks before generating machine code, eliminating branches that are false at compile time.
fn log[verbose: Bool](message: String):
@parameter
if verbose:
print("[DEBUG]", message)
fn main():
log[True]("Starting computation") # prints the debug line
log[False]("Starting computation") # compiled to nothing — zero cost
@value
Automatically generates the copy constructor, move constructor, and destructor for a struct. Without @value, you must write these yourself. With it, the compiler infers sensible defaults from the struct's fields.
@value
struct Point:
var x: Float64
var y: Float64
fn main():
var p1 = Point(1.0, 2.0)
var p2 = p1 # copy constructor auto-generated
p2.x = 99.0
print(p1.x) # 1.0 — p1 unchanged (true copy)
print(p2.x) # 99.0
@register_passable
Marks a small struct as passable in CPU registers rather than on the stack or heap. Structs that fit in one or two registers benefit from faster function call passing when annotated this way. SIMD-based types use this internally.
@register_passable("trivial")
struct Color:
var r: UInt8
var g: UInt8
var b: UInt8
var a: UInt8
fn blend(c1: Color, c2: Color) -> Color:
return Color(
(Int(c1.r) + Int(c2.r)) // 2,
(Int(c1.g) + Int(c2.g)) // 2,
(Int(c1.b) + Int(c2.b)) // 2,
(Int(c1.a) + Int(c2.a)) // 2,
)
fn main():
var red = Color(255, 0, 0, 255)
var blue = Color(0, 0, 255, 255)
var mixed = blend(red, blue)
print(mixed.r, mixed.g, mixed.b) # 127 0 127
Decorator Summary
Decorator | What it does -----------------------|---------------------------------------------- @always_inline | Paste function body at call site, no overhead @staticmethod | Method belongs to struct type, not instance @parameter | Evaluate at compile time, eliminate dead branches @value | Auto-generate copy/move/destroy @register_passable | Pass small struct in CPU registers @noncopyable | Prevent copying (enforce move-only semantics)
Key Takeaways
Decorators modify function and struct behavior using an @name annotation above the definition. @always_inline eliminates call overhead for small hot functions. @staticmethod creates type-level methods that need no instance. @parameter moves evaluation to compile time, removing runtime branching. @value auto-generates the boilerplate copy, move, and destroy methods. @register_passable lets small structs travel through function calls in CPU registers for maximum speed.
