Killing
How many times have we all caught ourselves writing code like this:
func makeNext() -> Int {
let tmp = state
state += 1
return tmp
}
We want to manipulate some state, but we also want to return it in its pre-manipulated condition. In other words we’d like to insert a bit of code right after our return statement:
// No, this doesn't work.
func makeNext() -> Int {
return state
state += 1
}
But we can’t because nothing after the return
will get executed. So instead we stick our state in a tmp
variable, do what we gotta do, and then return the tmp
.
So what? It’s just one more line of code, right?
The danger here (and with tmp variables everywhere) is that, by definition, they’re exact copies of types we’re actively working with in the same scope. How they differ isn’t semantically clear, and neither the compiler nor code review can stop us from accidentally substituting one for the other:1
func makeNext() -> Int {
let tmp = state
state += 1
return state //uh oh!
}
Thankfully Swift does provide a way for us to squeeze code in after the return
of a method — the documentation just doesn’t make this immediately obvious:
Use defer to write a block of code that is executed after all other code in the function…
Yay!
…just before the function returns.
Oh… Um… Well it turns out they’re talking about the function returning on the stack and not the return
statement executing in code. So doing something like this works perfectly:
func makeNext() -> Int {
defer {
state += 1
}
return state
}
This is true regardless of whether state
has value semantics or not.
Note this not only avoids confusing our original and tmp
variables, but now the intent of our code has been made clear:
Before we couldn’t know why we were forking off a tmp
unless we analyzed everything that messed with the original and followed it all the way through to the eventual return of its tmp
copy.2 Here, it’s clear from the get-go we want to do some stuff with state
. But first we’re going to return it.
1: Actually, in this trivial example, the compiler will warn us that tmp
is unused. But we can easily imagine situations where that will not be the case.↩︎
2: And then, if you’re anything like me, all the way back to the top to find where tmp
was made in an attempt to remember what it was before we changed it.↩︎