Defer Statement

Part of: Reef Language Reference Last reviewed on version: 0.8.0 Status: Implemented


Overview

The defer statement schedules code to execute when the enclosing function returns. Multiple defers execute in LIFO (last-in, first-out) order - like a stack.


Syntax

defer
    // statements to execute at function exit
end defer

Basic Usage

proc example()
    println("Start")

    defer
        println("Cleanup")
    end defer

    println("Middle")
    // "Cleanup" executes here, after function body
end example

Output:

Start
Middle
Cleanup

LIFO Order

Multiple defers execute in reverse order of registration:

proc test_lifo()
    defer
        println("First registered, last to run")
    end defer

    defer
        println("Second registered, runs second")
    end defer

    defer
        println("Third registered, runs first")
    end defer

    println("Function body")
end test_lifo

Output:

Function body
Third registered, runs first
Second registered, runs second
First registered, last to run

With Return Statements

Deferred code executes before every return path:

fn compute(x: int): int
    defer
        println("Cleanup before return")
    end defer

    if x < 0
        return -1  // defer runs here
    end if

    if x == 0
        return 0   // defer runs here
    end if

    return x * 2   // defer runs here
end compute

The return value is computed and captured before deferred blocks run — a defer that mutates state the return expression read cannot change the value being returned. This holds uniformly across plain functions, struct and trait-impl methods, Active Object methods, and lambdas.

A lambda body is its own defer scope: a defer registered inside a lambda runs when the lambda returns (explicitly or via its trailing expression), never at the enclosing function's exit — and the enclosing function's defers do not run at the lambda's exit.


Use Cases

1. Resource Cleanup

proc process_file(path: string)
    let file = open_file(path)

    defer
        close_file(file)
    end defer

    // Work with file...
    // File is closed no matter how we exit
end process_file

2. Lock Management

proc critical_section()
    acquire_lock()

    defer
        release_lock()
    end defer

    // Critical code...
    // Lock always released
end critical_section

3. Logging

proc operation()
    println("Operation started")

    defer
        println("Operation finished")
    end defer

    // Do work...
end operation

Multiple Resources (LIFO Advantage)

LIFO order naturally handles nested resource acquisition:

proc nested_resources()
    println("Acquiring resource A")
    defer
        println("Releasing resource A")
    end defer

    println("Acquiring resource B (needs A)")
    defer
        println("Releasing resource B")
    end defer

    println("Using both resources")
end nested_resources

Output (resources released in correct order):

Acquiring resource A
Acquiring resource B (needs A)
Using both resources
Releasing resource B
Releasing resource A

Scope

Reef's defer is function-scoped, not block-scoped:

proc example()
    if some_condition
        defer
            println("This runs at function exit, not block exit")
        end defer
    end if

    // More code...
    // Defer runs here at function end
end example

Comparison with Other Languages

Language Syntax Scope
Reef defer ... end defer Function
Go defer func() Function
Swift defer { ... } Block
Zig defer ...; Block

Reef's function-scope defer is simpler and matches Go's semantics.


Best Practices

1. Register Defers Early

proc good()
    let resource = acquire_resource()
    defer
        release_resource(resource)
    end defer
    // Use resource safely
end good

2. Pair Acquire/Release

// Each acquire immediately followed by its defer release
let a = acquire_a()
defer
    release_a(a)
end defer

let b = acquire_b()
defer
    release_b(b)
end defer

3. Keep Defers Simple

// GOOD: Simple cleanup
defer
    close_file(f)
end defer

// AVOID: Complex logic in defer
defer
    if condition
        // complex cleanup...
    end if
end defer

Implementation Notes

  • Defer statements are collected during code generation
  • At each return point, all registered defers are emitted in LIFO order
  • At implicit function end, remaining defers are emitted
  • Zero runtime overhead when no defers are used

Next: 070_SPAWN.md Previous: 060_CLOSURES.md