简体   繁体   中英

Converting non-escaping value to 'Any' may allow it to escape error - within not escaping function

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM