The following code snippets are citied from https://www.avanderlee.com/swift/nonisolated-isolated/
actor BankAccountActor {
enum BankError: Error {
case insufficientFunds
}
var balance: Double
init(initialDeposit: Double) {
self.balance = initialDeposit
}
func transfer1(amount: Double, to toAccount: BankAccountActor) async throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
await toAccount.deposit(amount: amount)
}
func transfer2(amount: Double, to toAccount: isolated BankAccountActor) async throws {
guard balance >= amount else { --------- first error
throw BankError.insufficientFunds
}
balance -= amount. -------------------- second error
toAccount.balance += amount
}
func deposit(amount: Double) {
balance = balance + amount
}
}
Please correct me if I am wrong. My understanding of the 'transfer1' method is that it involves two actor instances. The first instance(self) and the second instance(toAccount). The first instance checks its balance and modify it within its isolated context. However, when calling the deposit method of the second instance, the 'transfer1' method of the first instance is non-isolated for the second instance. By marking transfer1 as async, it becomes an isolated context for the second instance (toAccount), allowing the deposit method to be safely executed within its own actor context.
Additionally, I am confused why two error messages appear on the 'transfer2' method.
1st error: Expression is 'async' but is not marked with 'await'.
2nd error: Actor-isolated property 'balance' can not be mutated on a non-isolated actor instance.
The answer is in SE-0313.
In brief, a function can be isolated to only a single actor. When no isolated
keyword is specified (for a member function in an actor), the isolation defaults to self
. When the keyword is specified on a parameter, the isolation is given to that parameter's actor.
So, in your example, transfer1
is (implicitly) isolated to self
, while transfer2
is (explicitly) isolated to toAccount
. That means balance
(aka self.balance
) needs an await
in transfer2
.
Note that a function cannot be isolated to two different actors (in the sense of two different actor instances, even of the same actor type) at the same time. That's why there can only be one parameter marked isolated
.
Finally, the fact that these are async
functions is irrelevant to the isolation part of the story.