Capture noncopyable in closure

Hi there, I'm experimenting with noncopyable types in Swift and encountered a problem when trying to consume a value within a closure. Here's a minimal reproduction:

struct Foo: ~Copyable {
}

func withBlock(_ block: () -> Void) {
    block()
}

func playground() async throws {
    let foo = Foo()  // Noncopyable 'foo' cannot be consumed when captured by an escaping closure
    withBlock {
        consume foo
    }
}

It seems like Swift does not support moving capture. Is this a known limitation of the current implementation of closure (e.g., no FnOnce like Rust)?

I can think of an inconvenient workaround to explicitly consume the value in function parameters:

func withComsumingBlock<T: ~Copyable>(
    _ t: consuming T,
    block: (consuming T) -> Void
) {
    block(t)
}

func playground() async throws {
    let foo = Foo()
    withComsumingBlock(foo) { foo in
        consume foo
    }
}

This pattern changes the callee API, and I need to write boilerplate extensions for external libraries. Is there any better workaround in Swift 6.2+ or future SE for this? Thanks!

1 Like

You can use the same basic workaround as Rust when you're stuck with a FnMut: wrap in an Optional, check on unwrap. Definitely still feels like a workaround, but it's unclear if/when Swift will get a FnOnce equivalent.

func playground() async throws {
    var foo: Optional = Foo()
    withBlock {
        let foo = foo.take()! // or guard let, whatever
        consume foo
    }
}
6 Likes