Skip to content

Defer and Cleanup

A defer always runs at the end of a scope at any point after it is declared, defer is commonly used to simplify code that needs clean-up; like closing unix file descriptors, freeing dynamically allocated memory or closing database connections.

The end of a scope also includes return, break, continue or rethrow !.

fn void test()
{
io::printn("print first");
defer io::printn("print third, on function return");
io::printn("print second");
return;
}

The defer runs after the other print statements, at the function return.

When there are multiple defer statements they are executed in reverse order of their declaration, last-to-first declared.

fn void test()
{
io::printn("print first");
defer io::printn("print third, defers execute in reverse order");
defer io::printn("print second, defers execute in reverse order");
return;
}
import std::io;
fn char[]? file_read(String filename, char[] buffer)
{
// return Excuse if failed to open file
File file = file::open(filename, "r")!;
defer {
io::printn("File was found, close the file");
if (catch excuse = file.close())
{
io::printfn("Fault closing file: %s", excuse);
}
}
// return if fault reading the file into the buffer
file.read(buffer)!;
return buffer;
}

If the file named filename is found the function will read the content into a buffer, defer will then make sure that any open File handlers are closed. Note that if a scope exit happens before the defer declaration, the defer will not run, this a useful property because if the file failed to open, we don’t need to close it.

A defer try is called at end of a scope when the returned Optional contained a result value.

fn void? test()
{
defer try io::printn("✅ defer try run");
// Returned an Optional result
return;
}
fn void main(String[] args)
{
(void)test();
}

Function returns an Optional result value, this means defer try runs on scope exit.

fn void? test()
{
defer try io::printn("❌ defer try not run");
// Returned an Optional Excuse
return io::FILE_NOT_FOUND?;
}
fn void main(String[] args)
{
if (catch err = test())
{
io::printfn("test() returned a fault: %s", err);
}
}

Function returns an Optional Excuse, this means the defer try does not run on scope exit.

A defer catch is called at end of a scope when exiting with an Optional Excuse, and is helpful for logging, cleanup and freeing resources.

defer catch { ... }
defer (catch err) { ... };

When the fault is captured this is convenient for logging the fault:

defer (catch err) io::printfn("fault found: %s", err)
import std::core::mem;
fn char[]? test()
{
char[] data = mem::new_array(char, 12)!;
defer (catch err)
{
io::printfn("Excuse found: %s", err)
(void)free(data);
}
// Returns Excuse, memory gets freed
return io::FILE_NOT_FOUND?;
}