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.

Leave a Comment

Your email address will not be published. Required fields are marked *