Mutable closures can capture themselves?

Just ran into an interesting question on StackOverflow which surprised me.

var closure = { print("initial definition") }

closure() // prints "initial definition"

closure = {
    print("second definition")
    closure() // This ends up referencing this new definition, not the initial one
}

closure() // prints "second definition" repeatedly, until the stack overflows 💣

Why does the reference to closure capture the new value (which isn't assigned yet), instead of the initial value?

Here's my hypothesis: The new closure is capturing the variable closure (the reference, not the referred-to closure object). The variable is then mutated, so the captured variable observes this change, and refers to the new closure object.

Is my hypothesis correct? Is it expected behavior? Should there be a warning about this (similar to how closures can't refer to themselves in their own initialization)?

That sounds about right. If you want to "make a copy", you can put it in the capture list:

var closure = { print("initial definition") }

closure() // prints "initial definition"

closure = { [closure] in
    print("second definition")
    closure()
}

closure()
// second definition
// initial definition
5 Likes

This technique can be used legitimately to make recursive closures, so I don't think it deserves a warning in general because recursive function calls don't have one. However, recursive function calls do have a warning for this specific case (“All paths through this function will call itself”) and it would be nice if recursive closures got the same treatment.

3 Likes