What does "Converting non-escaping parameter '{x}' to generic parameter 'T' may allow it to escape" means?

I have this code

typealias MyFunc = (Int) -> Bool

func isOdd(x: Int) -> Bool {
    return x % 2 != 0
}

func myOtherFunc(_ delegate: MyFunc) {
    withUnsafePointer(to: delegate, { callback in /* And here's the error */
        print("Type of: \(String(describing: type(of: callback)))")
    })
    print("Yay")
}

myOtherFunc(isOdd)

And I'm seeing this error: Converting non-escaping parameter 'delegate' to generic parameter 'T' may allow it to escape

Any idea what that means and why is happening?

By default in Swift, parameters with function type (such as delegate) are "non-escaping," which means that they aren't permitted to be used outside the body of the function itself. This is useful because it allows you to make assumptions like "this method won't retain a reference to the closure I pass it, so I won't accidentally create a reference cycle."

However, the compiler only really performs this analysis for function types. This means that other types are assumed to always be escaping—you are free to store class references however you like, copy value types around, etc. The signature of withUnsafePointer(to:_:) looks like this:

func withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

Since the value parameter has type T, and not a function type, the compiler must assume that within the body of withUnsafePointer, the value parameter may escape. This means it's invalid to pass an escaping closure as the argument for the value parameter.

There are two workarounds here. One is to make the delegate parameter to myOtherFunc escaping, which would allow you to use it in potentially-escaping contexts within the body of myOtherFunc. The other is to make use of the withoutActuallyEscaping(_:do:) function, which allows a non-escaping closure to be used in escaping contexts (as long as the closure doesn't actually escape in practice).

From what you have here, it looks like withoutActuallyEscaping(_:do:) would be acceptable (since the pointer in withUnsafePointer is never supposed to escape the body of the closure anyway), but depending on what you expect myOtherFunc in the future you will have to be careful that delegate never escapes.

8 Likes

Thanks, this actually did the deed.

Code now looks like this:

func myOtherFunc(_ delegate: MyFunc) {
    withoutActuallyEscaping(delegate, do: { delegate in
        withUnsafePointer(to: delegate, { callback in
            print("Type of: \(String(describing: type(of: callback)))")
        })
    })
    print("Yay")
}
1 Like

Awesome, glad that suited your needs! Just keep in mind that using withoutActuallyEscaping(_:do:) is a promise that as you evolve myOtherFunc you’ll never use escaping version of the function in a way that actually does escape. The compiler will no longer verify this for you!

This is a the thing. This is a public API, what I intend to do in the body of my function (the implementation) won't have the function escaping, so, it should, communicate to the user that this function is not meant to escape. This is my problem with the other workaround, I don't want to mark the function as escaping, cause I want the user of the API to know the function won't escape.

1 Like