Reputation: 43
My question is derived from the following Japanese question. It's not my question, but I'm trying to answer the following problem but I cannot find the suitable answer.
https://teratail.com/questions/298998
The question above will be simpled like below.
func executetwice(operation:() -> Void) {
print(operation)
operation()
}
This compiler required to add @escaping
keyword after operation:
label, such as
func executetwice(operation: @escaping () -> Void) {
print(operation)
operation()
}
But in fact, it seems that operation
block does not escape from this block.
Another way,
func executetwice(operation:() -> Void) {
let f = operation as Any
operation()
}
also compiler requires to add @escaping
keyword. It is just upcasting to Any
.
In other case, just casting to same type, it seems to be error.
func executetwice(operation:() -> Void) {
let f = operation as () -> Void //Converting non-escaping value to '() -> Void' may allow it to escape
operation()
}
I'm not sure why I need to add @escaping
keyword with no escaping condition.
Just adding @escaping
keyword will be Ok, but I would like to know why the compiler required the keyword in this case.
Upvotes: 4
Views: 662
Reputation: 270790
print
accepts (a variable number of) Any
as arguments, so that is why it's saying that you are converting a closure to Any
when you pass it to print
.
Many checks are applied on closure-typed parameters to make sure a non-escaping closure don't escape (for what it means for a closure to "escape", read this):
var c: (() -> Void)?
func f(operation:() -> Void) {
c = operation // compiler can detect that operation escapes here, and produces an error
}
However, these checks are only applied on closure types. If you cast a closure to Any
, the closure loses its closure type, and the compiler can't check for whether it escapes or not. Let's suppose the compiler allowed you to cast a non-escaping closure to Any
, and you passed it to g
below:
var c: Any?
func g(operation: Any) {
// the compiler doesn't know that "operation" is a closure!
// You have successfully made a non-escaping closure escape!
c = operation
}
Therefore, the compiler is designed to be conservative and treats "casting to Any
" as "making a closure escape".
But we are sure that print
doesn't escape the closure, so we can use withoutActuallyEscaping
:
func executetwice(operation:() -> Void) {
withoutActuallyEscaping(operation) {
print($0)
}
operation()
}
Casting a closure to its own type also makes the closure escape. This is because operation as () -> Void
is a "rather complex" expression producing a value of type () -> Void
. And by "rather complex" I mean it is complex enough that when passing that to a non-escaping parameter, the compiler doesn't bother to check whether what you are casting really is non-escaping, so it assumes that all casts are escaping.
Upvotes: 1