Null safety and memory, 4 of 5
Defer
Schedule cleanup to run when a block ends, however it ends.
What you’ll build
Three small programs that clean up after themselves: one that opens a connection and closes it
on every path out of a function, an early Return included; one that sets up and takes down in
the right order; and one that reports how far its work got. Defer puts the cleanup right after
the code that needs it, so it can’t be forgotten when the code gains another way out.
Run something at the end of a block
Defer takes one statement, and runs it when the block it’s in ends: at its last line, or
through Return, Break, Continue, or an error. Here the connection is closed on both paths out of
Fetch.
Import std.core
Class Connection
ReadOnly Field Name: String
New(name: String)
Name = name
Print("open {name}")
End New
Method Close()
Print("close {Name}")
End Method
End Class
Function Fetch(name: String, cached: Bool): String
Local connection = Connection(name)
Defer connection.Close()
If cached Then Return "(from the cache)"
Print("reading from {name}")
Return "the data from {name}"
End Function
Print(Fetch("db.example", False))
Print(Fetch("db.example", True))open db.example reading from db.example close db.example the data from db.example open db.example close db.example (from the cache)
The value to return is worked out before the deferred statement runs, so a Defer can’t change what a function returns.
Several Defers
A block’s Defers run in reverse order, the last one first, so what was set up last is taken down first. A loop’s body is a block, so each pass runs its own Defers at its end.
Import std.core
Function Setup()
Print("connect")
Defer Print("disconnect")
Print("log in")
Defer Print("log out")
Print("work")
End Function
Setup()
For pass = 1 To 2
Defer Print("end of pass {pass}")
Print("pass {pass}")
End Forconnect log in work log out disconnect pass 1 end of pass 1 pass 2 end of pass 2
Values as they are at the end
A deferred statement’s values are read when it runs, not when the Defer is reached. Here it
shows count as it is when the block ends.
Import std.core
Function Process(items: String[])
Local count = 0
Defer Print("processed {count} of {items.Count}")
For item In items
If item == "" Then Return
count++
End For
End Function
Process(["a", "b", "c"])
Process(["a", "", "c"])processed 3 of 3 processed 1 of 3
A Defer runs on errors too, which Finally and Defer shows.
It doesn’t run on a panic, which ends the program at once.
What you learned
Defer statementruns the statement when its block ends, however it ends.- A block’s Defers run the last first, and each pass of a loop runs its own.
- A deferred statement reads its values when it runs.
- The returned value is worked out before the Defers run.
- A panic ends the program without running Defers.
The rules are in the language reference.
Report an issue with this page or with FutureBlitz on GitHub.