Escaping block as argument to an escaping argument

Viewed 77

I'd like to schedule jobs, and pass a completion handler to each job like so:

class Scheduler {
    var jobs = [(() -> ()) -> ()]()

    func schedule(block: @escaping (() -> ()) -> ()) {
        jobs.append(block)
    }

    func run() {
        if !jobs.isEmpty {
            let job = jobs.removeFirst()
            // Run the next job when this job is done
            job { self.run() }
        }
    }
}

func test() {
    let s = Scheduler()
    s.schedule { next in
        // Perform some work, this requires `next` to be escaping:
        DispatchQueue.main.async(execute: next)
    }
    // later
    s.run()
}

The swift compiler does not like that next is escaping, without being marked as such:

main.swift:22:43: error: passing non-escaping parameter 'next' to function expecting an @escaping closure
        DispatchQueue.main.async(execute: next)
                                          ^
main.swift:21:28: note: parameter 'next' is implicitly non-escaping
    Scheduler().schedule { next in
                           ^

The obvious change would be to mark the next argument as escaping:

func schedule(block: @escaping (@escaping () -> ()) -> ()) {

But now the compiler seems to be mistakenly mark the block as non-escaping:

main.swift:9:21: error: using non-escaping parameter 'block' in a context expecting an @escaping closure
        jobs.append(block)
                    ^
main.swift:8:19: note: parameter 'block' is implicitly non-escaping
    func schedule(block: @escaping (@escaping () -> ()) -> ()) {
                  ^
                         @escaping

Is this a known compiler issue? Are there workarounds?

0 Answers
Related