Hare by Example: Defer

Defer allows us to specify a function to execute once the current function being executed returns. The order in which defers occur in reverse order in which they are declared. This also gives us a nice way to perform cleanup tasks.

use fmt;

fn cleanup(data: []u8) void = {
	fmt::println("Do the cleanup work if something bad happens")!;
	free(data);
};

export fn main() void = {
	// As the line states this function is defined first, but will
	// be the last to execute in the defer chain.
	defer fmt::println("Defined first, but called last")!;

	fmt::println("Doing some more work here")!;

	// We define anothe defer here that will be called before the
	// defer above.
	defer fmt::println("Defined second, but called second to last")!;

	// We can use defer as a cleanup pattern in case something goes
	// wrong during processing. We can allocate some memory of data.
	const data: []u8 = alloc([], 10z)!;

	// We'll set a local boolean to keep track if this are ok or
	// not. Immediately after that we define a defer with an if to
	// check if ok's value changed. If it changed to false then the
	// cleanup function runs as part of the defer chain.
	let ok = true;
	defer if (!ok) cleanup(data);

	// Further down we can perform some logic that might cause ok's
	// value to change to false. This could also be when we try to
	// write data to a file and we match on an error and we set
	// ok's value to false to ensure cleanup runs.
	if (len(data) < 100z) {
		ok = false;
	};

	fmt::println("We're all done")!;
};
$ hare run defer.ha
4/4 tasks completed (100%)
Doing some more work here
We're all done
Do the cleanup work if something bad happens
Defined second, but called second to last
Defined first, but called last