Search code examples
swiftswift-concurrency

Actor-isolated property cannot be passed 'inout' to 'async' function call


I'm new (like most everyone, I suppose) to Swift concurrency, and I'm running into a compiler error I don't know what to do with.


struct Thing {
  var counter = 0
  mutating func increment() async {
    counter += 1
  }
}

class Controller: UIViewController {
  var thing = Thing()
  
  func mutate() async {
    await thing.increment()
    print(thing.counter)
  }
}

let c = Controller()
Task {
  await c.mutate()
}

The first line of the mutate() function gives me the following error. Actor-isolated property 'thing' cannot be passed 'inout' to 'async' function call

If I just inherit from class instead of UIViewController things work fine, but I need the controller here, so I need to figure out how to make this work in that specific context.


Solution

  • I think the issue comes from Thing being a struct. A mutating func on a struct will assign a new value to the thing property on the Controller. In order for that to work, thing is treated as an inout parameter in the call to thing.increment().

    If you make thing an actor instead of a struct then increment()won't need to be a mutating func and so thing won't be treated as an inout parameter.


    A possible workaround is to make a copy of the struct first, then call the mutating func on the copy, then store it back on the property in the controller.

    func mutate() async {
        var thing = self.thing
        await thing.increment()
        self.thing = thing
        
        print(thing.counter)
    }
    

    The reason it's an issue is the UIViewControllers are all actors now, so the properties are considered actor isolated. There is a nonisolated keyword but it cant be applied to stored properties so it doesn't seem to help here.

    If the controller is changed to be an actor, the error message changes a bit to say that.

    error: cannot call mutating async function 'increment()' on actor-isolated property 'thing'
            await thing.increment()
                        ^