Borrowing nested withUnsafePointer: cannot be captured by an escaping closure since it is a borrowed parameter

have two borrowed parameters of type SDL_FRect, need to convert both to UnsafePointer<SDL_FRect>, but nested closures and borrowing parameters do not like each other

    func drawTexture(
        _ texture: SDLTexture,
        _ srcrect: borrowing SDL_FRect?,
        _ dstrect: borrowing SDL_FRect
    ) -> Bool {
        return withUnsafePointer(to: dstrect) { ptrDstRect in
            if srcrect == nil {
                return SDL_RenderTexture(ptr, texture.ptr, nil, ptrDstRect)
            } else {
                // this nested withUnsafePointer(to: localSrcRect) is not allowed
                return withUnsafePointer(to: srcrect!) { ptrSrcRect in
                    SDL_RenderTexture(ptr, texture.ptr, ptrSrcRect, ptrDstRect)
                }
            }
        }
    }

throws error

'srcrect' cannot be captured by an escaping closure since it is a borrowed parameterSourceKit

renderer.swift(36, 47): Closure capturing 'srcrect' here

Definition

public struct SDL_FRect {

    public init()

    public init(x: Float, y: Float, w: Float, h: Float)

    public var x: Float

    public var y: Float

    public var w: Float

    public var h: Float
}

is there a way to use borrowed parameters in nested withUnsafePointer closures?

1 Like

Are you sure that's where that error is coming from? The closure withUnsafePointer accepts isn't escaping. Is there self-contained code sample you can provide that results in the same error?

reference bug repo GitHub - 3xau1o/swift-sdl-demo

line with workaround swift-sdl-demo/Sources/Ludo/renderer.swift at 8600958760c5691c5ab6d723a4966fbb31e4acf7 · 3xau1o/swift-sdl-demo · GitHub

Isn't this unsafe / verboten in any case as you are escaping the unsafe pointers out of the closure by returning them to the caller?

Sorry, I cannot reproduce your error, with the following reduced code: Compiler Explorer.

1 Like

@CrystDragon here is your swift web compiler updated to match the code with compiler issue

the function signature must be updated

- func foo(_ ptr1: UnsafeRawPointer, _ ptr2: UnsafeRawPointer) {}
+ func foo(_ ptr1: UnsafeMutablePointer<NC>, _ ptr2: UnsafeMutablePointer<NC>) {}

that's what break it, seems UnsafeMutablePointer had different borrowing implications than UnsafeRawPointer, it works though if change parameters from borrowing to inout

Please post code as text, not screenshots. Your original code:

struct NC: ~Copyable {}

func drawTexture(srcrect: borrowing NC?, dstrect: borrowing NC) {
    withUnsafePointer(to: dstrect) { ptrDstRect in
        if let src = srcrect {
            withUnsafePointer(to: &src) { ptrSrcRect in
                foo(ptrSrcRect, ptrDstRect)
            }
        } else {
            foo (nil, ptrDstRect)
        }
    }
}

func foo (_ ptr1: UnsafePointer<NC>!, _ ptr2: UnsafePointer<NC>) { }

You cannot assign srcrect to a var because it is a borrow argument. You will need to make it inout.

However, this leads to a different issue that I don’t understand:

struct NC: ~Copyable {}

func drawTexture(srcrect: inout NC?, dstrect: borrowing NC) {
  //             `- error: missing reinitialization of closure capture 'srcrect' after consume
    withUnsafePointer(to: dstrect) { ptrDstRect in
        if var src = srcrect {
  //    `- note: consumed here
            withUnsafePointer(to: &src) { ptrSrcRect in
                foo(ptrSrcRect, ptrDstRect)
            }
            srcrect = consume src
        } else {
            foo (nil, ptrDstRect)
        }
    }
}

func foo (_ ptr1: UnsafePointer<NC>!, _ ptr2: UnsafePointer<NC>) { }

I clearly reassigned to srcrect.

The point was to show the compiler error

here is the playground code without if let/var and borrowing, it compiles only for ~Copyable structs, the issue is that even though this compiles, the real code does not work because structs are generated without ~Copyable on swift swift's C Import

struct NC: ~Copyable {}

func drawTexture(
    _ srcrect: borrowing NC?,
    _ dstrect: borrowing NC
) {
    withUnsafePointer(to: dstrect) { ptrDstRect in
      if srcrect != nil {
        withUnsafePointer(to: srcrect!) { ptrSrcRect in
            foo(ptrSrcRect, ptrDstRect)
        }
     } else {
        foo(nil, ptrDstRect)
     }
    }
}

func foo(_ ptr1: UnsafePointer<NC>!, _ ptr2: UnsafePointer<NC>!) {}

If this is changed the Swift, the final error appears

- struct NC: ~Copyable {}
+ struct NC {}

So in summary C Imports on swift are mutable, do not play well with borrowing and ~Copyable structs, there is no way to customize the generated c bindings, and may be not that bad to use inout for C interop

It has nothing todo with UnsafeMutablePointer.

The first issue in your updated code is that for a non-copyable, you cannot write var src = and use it in &src. Because the right-hand-side of "=" must be consumable, which conflicts with this RHS value being capture into a closure (it must remain to be borrowing).

The second issue is the compiler does not support borrowing for optional binding statements (eg. if let src = ). You have to reply on the feature "Borrowing Switch".

The following code will compile:


func drawTexture(
    _ srcrect: borrowing NC?,
    _ dstrect: borrowing NC
) {
    withUnsafePointer(to: dstrect) { ptrDstRect in
        switch srcrect {
        case .none:
            foo(nil, ptrDstRect)
        case .some(let src):
            withUnsafePointer(to: src) { ptrSrcRect in
                foo(ptrSrcRect, ptrDstRect)
            }
        }
    }
}

func foo(_ ptr1: UnsafePointer<NC>!, _ ptr2: UnsafePointer<NC>!) {}

This feature relies on NC being a non-copyable, if that is not the case, I cannot come up with any workarounds for now. Maybe we have to wait for the feature of borrow operator.

1 Like