in this PR from a while back, various flavors of async inout
access to actor-isolated state were banned. for example, you can't do something like this:
struct S {
mutating func mutate() async {}
}
actor A {
var s = S()
func mutateState() async {
await s.mutate()
`- error: cannot call mutating async function 'mutate()' on actor-isolated property 's'
}
}
however, it doesn't appear that the asynchronicity itself is the issue, but rather the combination with the actor-isolated-ness of the mutated state, as this is allowed:
Task {
var s = S()
await s.mutate()
}
i'd like to better understand the 'why' behind this category of error. the referenced PR provides this rationale as motivation:
Passing actor state to async functions via inout parameters violates
[atomicity]
i'm not entirely sure what this is meant to convey, but my current theory is that the issue here has to do with the Law of Exclusivity. if actor-isolated state is passed inout
to an async function, then, were that function to suspend, the actor could resume some other work. but to not violate The Law, any such re-entrant operations would then be unable to access the portion of its state passed inout
to the suspended function. rather than attempt to deal with the complexities that would arise by allowing this, such situations are prohibited by a compiler error. Task
-isolated state however, does not have the re-entrancy issues to contend with, so the use of inout
arguments in async functions is okay.
is this the right way to think about this restriction? if not, what is?