Yes, absolutely.
The law of exclusivity!
This applies when you write a func that is both mutating and async. During the entire duration of the call to that function, you must not access the struct in question from any other Task, even when the call is waiting for an async function to complete.
In general, this isn't going to be something you need to think about, but it's worth bearing in mind in some interesting places. Consider a silly example:
struct AsyncDelayCounter {
private var count = 0
mutating func slowlyAdd() async -> Int {
self.count += 1
try! await Task.sleep(nanoseconds: 100_000_000)
return self.count
}
}
If you, say, store this value in a class and call it from multiple Tasks at once, you'll trip a runtime exclusivity violation and crash. In fact, if you store this in an actor like this:
actor ThreadSafeCounter {
private var delayCounter = AsyncDelayCounter()
init() { }
func addOne() async -> Int {
return await self.delayCounter.slowlyAdd()
}
}
Then the compiler will simply fail to compile:
test.swift:17:40: error: cannot call mutating async function 'slowlyAdd()' on actor-isolated property 'delayCounter'
return await self.delayCounter.slowlyAdd()
^
As a result, you want to think of structs with async methods in a very similar way to the way you think of structs with sync methods: they transform from state to state, and continue not to have identity. You still cannot share them across multiple tasks unless they are guaranteed not to mutate.
Not quite. Specifically:
Remember that self is always retained strongly by a method. Once you've fired off an async method on a struct you don't need to hold onto it.
Additionally:
This should not matter. If it matters, you are either calling a mutating func on the struct (which you definitionally cannot share with another Task without violating the law of exclusivity) or you have referentiality associated with the struct, in which case copying it won't matter.