Skip to content
Go Basics: Understand Deferred Calls Before Using Cleanup

Go Basics: Understand Deferred Calls Before Using Cleanup

October 31, 2026

Deferred cleanup is convenient, but the moment its arguments are evaluated matters. A tiny example makes the ordering visible without touching real files.

Prepare a scratch directory

Tested on Linux amd64 with Go 1.26.10. Install Go and make go available on PATH. You also need Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. The example uses only the standard library and needs no module or external dependency.

Keep the same terminal open. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as defer-evaluation-demo.go:

package main

import "fmt"

func demonstrate() {
	label := "opened"
	defer fmt.Println("Argument:", label)
	defer func() { fmt.Println("Closure:", label) }()
	defer fmt.Println("Last registered")
	label = "finished"
	fmt.Println("Body:", label)
}
func main() {
	demonstrate()
	fmt.Println("Returned")
}

Run it:

go run defer-evaluation-demo.go

Observed output:

Body: finished
Last registered
Closure: finished
Argument: opened
Returned

Read the result

The direct deferred call captures label while it still contains opened. The closure reads the variable when the deferred function executes, after it has changed. Deferred calls run in reverse registration order, after the function body and before its caller continues.

Apply it carefully

Register cleanup only after acquiring the resource successfully. Defers inside a loop wait until the surrounding function returns; use a per-item helper when each resource should close promptly. os.Exit does not run deferred functions. For writes, a close error can matter and should not be silently discarded.

The official Go reference documents the behavior used here.

Clean up

Remove the saved source, return to the starting directory, and remove the empty practice directory. go run does not leave an executable here; Go manages its build cache separately.

rm -- defer-evaluation-demo.go
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect its remaining contents before removing anything else.

Acknowledgements

This post was created with assistance from AI.

Last updated on