Hello, I'm new to Swift and I was looking at capture lists and their use cases for struct types. (Most information I've found online relate to how they're used to break reference cycles in classes and so on, but I wasn't able to put together if/when I might use them with structs)
So I was experimenting with capture lists, and ran into the following which surprised me:
struct SomeStruct {
var a: Int
var b: Int
var sum: Int {
return a + b
}
func getInnerClosure() -> () -> () {
return { print(self.sum) }
}
func getInnerClosureWithExplicitCapture() -> () -> () {
return { [self] in print(self.sum) }
}
}
var myStruct = SomeStruct(a: 3, b: 5)
let innerClosure = myStruct.getInnerClosure()
let innerClosureWithExplicitCapture = myStruct.getInnerClosureWithExplicitCapture()
let outerClosure = { print(myStruct.sum) }
let outerClosureWithExplicitCapture = { [myStruct] in print(myStruct.sum) }
innerClosure() // prints 8
innerClosureWithExplicitCapture() // prints 8
outerClosure() // prints 8
outerClosureWithExplicitCapture() // prints 8
myStruct.a = 99
innerClosure() // prints 8
innerClosureWithExplicitCapture() // prints 8
outerClosure() // prints 104
outerClosureWithExplicitCapture() // prints 8
Here I'm defining a simple struct with two fields and a property. The struct also has two methods which act like "closure factories", where the closures capture self
, and print out self.sum
. I've labelled these "inner closures". One of these use an explicit capture list to capture self
, and one does not.
At the same time, I'm creating the same closures in file-scope, by hand, which I'm referring to as "outer closures". Again, one with an explicit capture list and one without.
For the outer closures, the behavior seems to be that when the closed-over variable is included in the capture list, it is captured inside the closure "eagerly" (with a copy?) whereas not using a capture list results in a "lazy" capture. As in, if I modify myStruct
and then evaluate the closure, the new struct attribute values appear to be visible to outerClosure
.
I was expecting something similar for the "inner" closures that were generated by struct methods; namely that the capture lists control whether the capture is "lazy" or not. However it seems like regardless of the presence of [self]
in a capture list, self
is always captured eagerly.
Could you help me understand the difference in behavior I'm seeing. Is this expected? Thanks!