5

I read this question and answers and Cocoacasts blog post and I fully understand what is @escaping annotation.

But honestly I do not understand why we need it at all.

The above Cocoacasts blog post states that:

There are several benefits to make closures non-escaping by default. The most obvious benefits are performance and the ability for the compiler to optimize your code. If the compiler knows that a closure is non-escaping, it can take care of a many of the nitty-gritty details of memory management.

But Swift compiler can determine if @escaping is missing and shows an error in this case so if we remove @escaping annotation from Swift language then the compiler still can see when closure does not escape and we can make him apply optimizations in that case.

This also means that you can use the self keyword without issues in non-escaping closures because the closure is invoked before the function returns. There is no need to use a weak reference to self in the closure. This is a nice benefit you get for free.

But if closure parameter is marked as @escaping I can still pass closure that use strong reference to self and compiler does not show any warnings. Actually it would be more useful if all references captured in @escaping closure were weak by default and special keyword were applied to make them strong.

Also I thought that may be @escaping annotation is the way to make code self-documented by explicitly declaring that this closure parameter will not escaping the function body but what the purpose of that for the calling side? It does not restrict the way the closure defined and does not prevent the calling side from using strong references. So all I have left is the hope that the calling side will carefully look at function signature and will take appropriate actions (like using weak references).

So the question is why do we really need @escaping closure in Swift 3 and what are the cases where we could not do without it?

UPDATE:

I know that not escaping closure can not be passed to function which closure parameter marked as @escaping:

func testNoEscape(f: () -> ()) {
    f()
}

var storeF: (() -> ())?

func testEscaping(f: @escaping () -> ()) {
    storeF = f
}

func tryPassNoEscapeToEscaping(f: () -> ()) {
    testEscaping(f: f)
}

results in compilation error:

passing non-escaping parameter 'f' to function expecting an @escaping closure

But that's the only real restriction that @escaping closure brings and it looks like built around itself and does not give any other benefits.

UPDATE 2

Though I laid out my thoughts above correctly but my final question was inaccurate.

The real question is why do we need @escaping annotation if compiler can detect escaping closures by himself and @escaping annotation does not apply any restrictions to parameter values?

In my opinion it would be much more useful if compiler did not allow us to do some bad things in escaping closures like using self and other strong references. Or if escaping closures were some special type and we'd have to mention that when calling function with escaping closure parameter:

func f(c: () -> ()) { // c is escaping from f somehow
    // ...
}

f escaping { // have to use `escaping` keyword 
    // ...
}

So the calling side does not have to look at f signature to know that c is escaping because it will get compilation error if it tries to pass non-escaping closure as escaping closure parameter value.

In current implementation developers who want to use f in their code have to look at f signature to understand that c will escape which is not safe because that requires that anyone who write this code initially and modify it later must know f signature in details which is not reliable and such code is not self-documented.

I understand that maybe my question is not suitable for SO. Sorry about that.

If so I will close it later if I will not get answer from people who implemented escaping logic in Swift language and compiler.

Community
  • 1
  • 1
mixel
  • 22,724
  • 10
  • 111
  • 154
  • Have I answered your question? – Alexander Mar 03 '17 at 16:03
  • Alexander We had a very helpful discussion, thank you very much, but I think that question can be fully answered only by developers of Swift language who made this decision. I'm trying to reach them now and preparing Swift proposal based on our discussion and @matt answer and I will return to this question later. – mixel Mar 03 '17 at 18:03
  • you've moved the goal posts of your question. As it exists in its current form, it has been answered. Youre welcome to try to create a new post for your new question, but itll almost certainly be deleted for being off-topic. SO isnt a forum for discussing the minutia of the language designers' a decisions. – Alexander Mar 03 '17 at 18:08
  • @Alexander I updated the question by summarizing our discussion. Sorry that I asked the wrong question the first time, actually I did mention that I know how escaping closures work but do not understand why closure escaping is implemented in such way but screwed up with the actual question. You are right that SO is a place for more simple and clear questions. – mixel Mar 04 '17 at 00:18
  • 2
    That's not how stack overflow works. You can't just ask a question, have it answered, but then not accept the answer because it sprouted a follow up question. I've spend the time to walk you through a thorough answer to your original question, until we reached the point where you came up with a follow up question that is a) an almost trivial nitpick over syntactical minutia and b) asking what the original developers thoughts were behind their decisions. – Alexander Mar 04 '17 at 00:23
  • 1
    This edited question is now a pointless combination of syntactical minutia and speculation over designer intent. As such, I've cast a vote to close. – Alexander Mar 04 '17 at 00:24

3 Answers3

5

Non-escaping closures can do things that escaping closures cannot. They are quite different animals.

For example, a non-escaping closure can refer to a property of self without explicitly saying self. This is because, being non-escaping (i.e. it is executed immediately upon receipt), it is in no danger of capturing self in some tricky way and causing a retain cycle.

And, non-escaping closures can close over an inout parameter. But this would be meaningless for an escaping closure and is disallowed.

It would be very mysterious if a closure seemed sometimes to require self and sometimes not to require self, sometimes to permit closure over inout and sometimes not. The @escaping annotation makes such rule distinctions clear and consistent.

matt
  • 447,615
  • 74
  • 748
  • 977
  • Thank you for the answer. It still useful for those who deals with escaping closures and I upvoted it. I'm sorry that I formulated my question in wrong way. – mixel Mar 04 '17 at 00:12
  • Here's another recent answer of mine where the legality of doing something is clearly fixed between whether the closure is escaping or nonescaping: http://stackoverflow.com/a/42720770/341994 – matt Mar 10 '17 at 15:48
2

Perhaps there are other reasons, but the main one is to communicate that your closure will outlive the duration of the function you pass it into. That can have really complex ramifications to your program's behaviour, so it's important to make that clear.

Additionally, the "escapingness" of a closure is part of the public API. The compiler can't see into compiled libraries that are being called. Without the @escaping attribute publicly communicating the fact that the closure can't escape, the compiler couldn't deduce it for itself from the compiled code.

Alexander
  • 48,074
  • 8
  • 78
  • 121
  • Yes, but it's like writing a comment "do not pass the zero here, because the program will crash". It does not prevent you from passing any closure to function that have `@escaping` annotated closure parameter except the case when you are inside other function and your closure came from parameter passed to this function without `@escaping` annotation. – mixel Mar 01 '17 at 12:26
  • @mixel So there are really two questions at play here. 1) "Why does the escapability of a closure matter?" and 2) "Why is there an annotation that explicitly declares escapability?". Your post asks #2, but it won't make sense without #1 – Alexander Mar 01 '17 at 12:29
  • @mixel A closure is a reference type. When a closure *is* escaping, it can outlive the function it's called in, and live on to be stored for an indefinite amount of time, and be called indefinitely many times. This is an issue if a the closure captures any value types in the local scope of the function in which the closure was declared. If the closure can outlive that local scope, then those local value types must also be made to outlive that local scope. As a result, those value types must be boxed, by moving them onto the heap, and introducing reference counting – Alexander Mar 01 '17 at 12:32
  • @mixel There's an expense to this that can really add up. It's best to avoid it if possible. And it is possible to avoid this boxing process, but only if it can be guaranteed that the closure never escapes. In such a case, it's guaranteed that the closure is made, used, and deallocated before the local vars it captures go out of scope. Because of this, it's safe for the closure to directly reference those local vars on the stack, without needing to box them. – Alexander Mar 01 '17 at 12:34
  • @mixel Those 2 comments answer 1) "Why does the escapability of a closure matter?" . My answer addresses 2) Why is there an annotation that explicitly declares escapability?", which is what you directly ask. That comes down to "because it's necessary for conveying escapability information beyond the bounds of compiled binaries" – Alexander Mar 01 '17 at 12:35
  • Thanks for heap/stack explanation. But why can not this be done by compiler automatically? I'm not looking for explanation how escaping closures work - I already read documentation and blog posts about that, but for explanation why they implemented in such explicit way while the compiler could do this work implicitly and does not bother developers with `@escaping` annotation. About 2) point - but compiler can determine if closure is escaping during compilation of those compiled binaries. – mixel Mar 01 '17 at 13:00
  • @mixell Yes, the compiler *does* determine the escapingness of closures during the compilation of those binaries. That's how it enforces the `@escaping` attribute policy. But what if I give you a closed source, compiled library to use? How does your compiler know if my compiler inferred a closure as escaping or not? – Alexander Mar 01 '17 at 13:03
  • But that's the thing - the closure is recognised as escaping not by closure body but by usage of parameter in function that accepts this closure. So if function is located in compiled library then during compilation of that library closure parameter is already can be marked as escaping automatically by compiler based on usage of closure parameter in body of that function. And actual closure parameter value does not matter. – mixel Mar 01 '17 at 13:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/136961/discussion-between-alexander-and-mixel). – Alexander Mar 01 '17 at 13:19
0

The compiler can see into the code that you create, but cannot see into the code that you use as a part of pre-compiled frameworks (as of presently, there are no Swift frameworks due to ABI problems, but there will be in the future).

Maybe they could have applied this escaping requirement only to "public" functions, but it would be a bit inconsistent looking. Better just to insert the keyword via the tooltips.

user1122069
  • 1,579
  • 1
  • 21
  • 43