1

The mutating keyword allows a function on an enum to mutate itself, but is there a way to extend that ability to escaping closures?

I'm looking for a definition of the timer handler in start() below that will move MyTimer back to its .off state:

private enum MyTimer {
    case off
    case on(Date, Timer) // start time, timer
    
    mutating func start() {
        if case .on(_, let timer) = self {
            timer.invalidate()
        }
        self = .on(Date(), Timer.scheduledTimer(withTimeInterval: maxComparisonInterval, repeats: false) { _ in
            self.stop() // Error
        })
    }
    
    mutating func stop() {
        if case .on(_, let timer) = self {
            timer.invalidate()
            self = .off
        }
    }
}

The timer handler above causes a compiler error: "Escaping closure captures mutating 'self' parameter"

alltom
  • 3,162
  • 4
  • 31
  • 47
  • 1
    Imagine this: you call `start` on a _local variable_, and when the escaping closure gets called, the local variable is not alive anymore. This is fine if you are not mutating it, because the closure could hold an immutable copy. – Sweeper Jul 08 '21 at 00:28
  • Why are you using a value type for this in the first place? Why do you think this is appropriate? – Sweeper Jul 08 '21 at 00:39
  • If the answer's no, that's cool. – alltom Jul 08 '21 at 04:47
  • 1
    I remembered that I wrote [this answer](https://stackoverflow.com/questions/62657685/idiomatic-way-to-mutate-collection-asynchronously-in-swift/62658325#62658325). It's not exactly what you want, but it is close. If you adapt it, you can do `start { $0(&someTimer) }`, rather than `someTimer.start()`. – Sweeper Jul 08 '21 at 04:57
  • Nice. I'd settled on `t.start() { t.stop() }` but passing the call to stop() back as a block makes that easier to maintain. Thanks! – alltom Jul 08 '21 at 15:58

0 Answers0