Defer
The defer statement schedules code to run when the function exits.
Basic usage
fn main() {
"start" print nl
defer {
"cleanup" print nl
}
"middle" print nl
}
// Output:
// start
// middle
// cleanup
The deferred code runs after the function body completes.
Guaranteed cleanup
Defers run even when errors occur:
use io
fn process_file(path:str -- )! {
-> path // bind parameter
path io::Read io::open if {
-> file // bind file handle
defer {
// Always runs
file io::close
}
// Even if this fails, file gets closed
file do_something!
} else {
drop
"open failed" 1 panic
}
}
LIFO order
Multiple defers execute in reverse order (last in, first out):
fn main() {
defer {
"first" print nl
}
defer {
"second" print nl
}
defer {
"third" print nl
}
}
// Output:
// third
// second
// first
Resource management
Use defer to manage resources:
use mem
fn with_buffer() {
1024 mem::alloc! -> buf
defer {
buf mem::free
}
// Use buffer...
// It will be freed when function exits
}
Multiple resources
use io
use mem
fn copy_file(src:str dst:str -- )! {
-> dst -> src // bind parameters
src io::Read io::open if {
-> src_file // bind file handle
defer {
src_file io::close
}
dst io::Write io::create if {
-> dst_file // bind file handle
defer {
dst_file io::close
}
4096 mem::alloc! -> buf
defer {
buf mem::free
}
// Copy data...
} else {
drop
"create failed" 2 panic
}
} else {
drop
"open failed" 1 panic
}
}
Resources are released in reverse order of acquisition.
Defer with variables
Defers capture the current value of variables:
fn main() {
0 -> x
defer {
"x = " print x print nl
}
42 -> x // Changes x
// Defer uses x's value at time of execution (42)
}
// Output: x = 42
Common patterns
Transaction
fn transaction()! {
begin_transaction
defer {
is_error if {
rollback
} else {
commit
}
}
// Perform operations...
}
Logging
fn traced_operation(name:str -- ) {
-> name // bind parameter
"Entering " print name print nl
defer {
"Exiting " print name print nl
}
// Do work...
}
Defer vs finally
Unlike try/finally in other languages, defer is simpler:
- No special syntax for try blocks
- Just add defer where you acquire a resource
- Cleanup is guaranteed
// Other languages:
// try {
// file = open(...)
// ...
// } finally {
// file.close()
// }
// Quadrate:
fn example() {
"file.txt" open_file -> file
defer {
file close_file
}
// ... use file
}
Nested functions
Defers only apply to their own function:
fn outer() {
defer {
"outer cleanup" print nl
}
inner // inner's defers run during inner
"outer continues" print nl
}
fn inner() {
defer {
"inner cleanup" print nl
}
"inner work" print nl
}
fn main() {
outer
}
// Output:
// inner work
// inner cleanup
// outer continues
// outer cleanup
What's next?
Learn about Context for passing data through your program.