4

I've never really understood when to use @escaping in Swift. I understand what it does (i.e. the difference between escaping and non-escaping closures), but tbf I've always been relying on Xcode to tell me when to add the modifier to my argument.

My question is, why is it the case that @escaping only applies to non-nil closures? This is what I mean:

func someFunc(someArg: Int, callback: @escaping (Error?) -> Void) {
    DispatchQueue.global(qos: .background).async {
    ...
    }
}

In the above, if I don't add @escaping Xcode gives me an error. However, if I make callback optional like the following, keeping @escaping results in Xcode error:

// This is wrong (Xcode complains about @escaping)
func someFunc(someArg: Int, callback: @escaping ((Error?) -> Void)?) {
    DispatchQueue.global(qos: .background).async {
    ...
    }
}

Why is that? Thank you!

danqing
  • 3,348
  • 2
  • 27
  • 43

1 Answers1

2

As you may know, closure parameters, by default, cannot escape. You have to add @escaping to allow them to escape. See here for what it means for a closure to escape.

A more accurate wording would be that closures in function parameter position are non-escaping by default. As you know ((Error?) -> Void)? is syntactic sugar for Optional<(Error?) -> Void>. Here, the closure type is not in the "function parameter position". It is used as the generic argument of the generic type Optional. This is just my opinion, but don't you think @escaping Optional<(Error?) -> Void> seems to mark the optional as "escaping"?

For a more extreme case, what about tuples? How can I mark only the first item in a pair of closures as escaping? Also, if I have a T<(Error?) -> Void>, is it guaranteed that it would definitely store an instance of a closure? I mean, it's just a generic type:

class Foo<T> {}

let a: Foo<(Int) -> Int> = Foo()

What should @escaping do on this struct then?

Finally, hasn't a closure that is wrapped in an Optional already escaped to that Optional?

Perhaps because that is too many things to design, implement and test, the Swift team just made all closures in optionals and tuples, as well as other generic types escaping implicitly.

See also: SR-2444

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • The 2444 link is helpful. I think in the last paragraph you meant ...just made all closures ... escaping by default. Thanks for the answer! – danqing Nov 16 '19 at 14:00
  • @danqing yep, that’s what I meant. Thanks for spotting it! – Sweeper Nov 16 '19 at 14:01