Actor Isolated Keyword in Xcode 15 Beta

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.

Answered by DTS Engineer in 757364022

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.

Accepted Answer

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.

Actor Isolated Keyword in Xcode 15 Beta
 
 
Q