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