Reputation: 4109
Consider the following code:
func foo(inout success: Bool) -> (()->()) {
return { _ in
success = true
print (success)
}
}
var success = false
let closure = foo(&success)
closure() //prints "true"
print(success) //prints "false"
The closure appears to be creating a copy of success and does not change the original. Why is this taking place? I had assumed that the closure would point to the original because we are passing an inout
variable.
Upvotes: 13
Views: 8242
Reputation: 64002
This seems to be covered by Swift Evolution proposal 0035, and is considered a bug.
The document there refers to the inout
parameter to the function as "a shadow copy that is written back to the argument when the callee returns". This seems to mean that there is, in essence, a temporary variable named success
in the executing context of foo()
. The value of that temp is then put into the outer success
only when foo()
returns.
Since in your foo()
, the closure has not run when foo()
returns, the value of the "shadow copy" has not changed. The outer success
keeps its value.
The important part is that the closure has captured the shadow copy, not the outer success
as you expect. So when the closure finally runs, that variable's value does change, but it no longer has any connection to the original outer success
.
The proposal uses this snippet to demonstrate:
func captureAndEscape(inout x: Int) -> () -> Void { let closure = { x += 1 } closure() return closure } var x = 22 let closure = captureAndEscape(&x) print(x) // => 23 closure() print("still \(x)") // => still 23
Upvotes: 6
Reputation: 437532
It makes sense that this wouldn't update your success
variable because your inout
parameter is a parameter of foo
, not of the closure itself. You get the desired behavior if you make the inout
parameter a parameter of the closure:
var success = false
let closure = { (inout flag: Bool) -> () in
flag = true
print(flag)
}
closure(&success) //prints "true"
print(success) //prints "true"
This pattern also works with the function, too, as long as you keep the inout
parameter a parameter of the closure:
func foo() -> ((inout Bool)->()) {
return { flag in
flag = true
print (flag)
}
}
var success = false
let closure = foo()
closure(&success) //prints "true"
print(success) //prints "true"
You also get the desired behavior if you use a reference type:
class BooleanClass: CustomStringConvertible {
var value: Bool
init(value: Bool) {
self.value = value
}
var description: String { return "\(value)" }
}
func foo(flag: BooleanClass) -> (()->()) {
return {
flag.value = true
print (flag)
}
}
let success = BooleanClass(value: false)
let closure = foo(success)
closure() //prints "true"
print(success) //prints "true"
Upvotes: 10