The defer expression delays the execution of code until the current scope exits. You can use the defer expression to handle cleanup tasks like resetting variables. Even when there is an early exit (such as return or break) from the current scope, the expressions in a defer block will run as long as defer is encountered before the exit.
The following code shows how to use defer to reset a variable to zero while still using that same variable as a return value. In this function, RoundScore is returned and the expressions in the defer block run immediately after.
This means you do not need to create a temporary variable to save the value of RoundScore before it gets reset to zero.
OnRoundEnd<public>() : void =
var ScoreThisRound : int = AddRoundScoreToTotalScore()
Print("Points scored this round: {ScoreThisRound}")
<# Adds RoundScore to TotalScore and resets RoundScore to 0.
Returns the RoundScore added. #>
AddRoundScoreToTotalScore<public>() : int =
defer:
set RoundScore = 0
UpdateUI()
Defer Expression Use
You can use a defer expression within any sequential code block such as a block, loop, for, if, branch, or even another defer.
Expressions within a defer block must be immediate (and not async) — with one exception. Async expressions can still be used within a defer if they are made immediate by using:
branch(if thedeferis within an async block such as in a coroutine)
A defer has no result, and cannot be used as an argument or an assignment value.
| defer | defer before an exit |
|---|---|
Verse | Verse |
Click image to enlarge. | Click image to enlarge. |
A defer expression will only execute if it is encountered before an early exit occurs.
| defer with early return | defer with a canceled async expression |
|---|---|
Verse | Verse |
Click image to enlarge. | Click image to enlarge. |
Multiple defer expressions appearing in the same scope accumulate. The order they are executed is the reverse order they are encountered — first-in-last-out (FILO) order. Since the last encountered defer in a given scope is executed first, expressions inside that last encountered defer can refer to context (such as variables) that will be cleaned up by other defer expressions that were encountered earlier and executed later.
Verse does not have deterministic destruction, but defer allows behavior similar to RAII to ensure cleanup.
| Multiple defer expressions in a code block | Multiple defer expressions in different code blocks |
|---|---|
Verse | Verse |
Click image to enlarge. | Click image to enlarge. |
Exiting early is allowed within a defer block as long as the exit does not transfer control outside the scope of the defer. For example, using a loop with break is allowed within a defer, but that break must keep the code execution within the defer block. It cannot refer to a loop outside of the defer block.
Any variables that have been encountered in the outer nesting scope of a defer can be used within that defer expression.
Remember that defer runs last at the time of scope exit. This means that it uses whatever the state of the program is (including variable values) at that time, not at the time when the defer is encountered. The code below will print 10 because defer runs immediately after MyScore is set to 10.
var MyScore = 5
defer:
Print(MyScore)
set MyScore = 10Using a defer expression as the last expression within a scope is the same as not using it at all. For example, these two sets of expressions will run in exactly the same order, so defer is not needed:
| Without defer | With defer |
|---|---|
Verse | Verse |