Unable to work properly with GlobalActor vars/functions within actor in Swift 6 mode

Assume having a code like this:

@globalActor
public struct FooActor: GlobalActor {
    public actor ActorType { }

    public static let shared: ActorType = ActorType()
}

@MainActor
protocol SomeMainActor {
    var value: Int { get }
    @FooActor var protectedStorage: [String] { get set }
}

final class Test: SomeMainActor {
    let value: Int = 0
    @FooActor var protectedStorage: [String] = []
}

actor TestActor {
    @MainActor private var someVar: SomeMainActor?
    init(withVar: SomeMainActor?) {
        self.someVar = withVar // Main actor-isolated property 'someVar' can not be mutated from a non-isolated context
    }

    @MainActor
    var value: Int? {
        self.someVar?.value
    }
    func add() {
        Task { @FooActor in
            await self.someVar?.protectedStorage.append("test") // Non-sendable type '(any SomeMainActor)?' in implicitly asynchronous access to main actor-isolated property 'someVar' cannot cross actor boundary
        }
    }
}

let test = TestActor(withVar: Test())
await test.add()

Tried different workarounds for init, like the code below. having errors like these:

init(withVar: SomeMainActor?) {
        Task { @MainActor in // 'self' captured by a closure before all members were initialized
            self.someVar = withVar // Sending 'someVar ' risks causing data races
        }
    }

(Couldn't reproduce this in playground, but got on a real project I try to migrate)

If I remove @MainActor from someVar or value getter, I get this errors:

actor TestActor {
    private var someVar: SomeMainActor?
...
    @MainActor
    var value: Int? {
        self.someVar?.value // Actor-isolated property 'someVar' can not be referenced from the main actor
    }

    var value: Int? {
        self.someVar?.value // Main actor-isolated property 'value' can not be referenced on a non-isolated actor instance
    }

    var value: Int? {
        get async {
            await self.someVar?.value // Non-sendable type 'any SomeMainActor' passed in implicitly asynchronous call to main actor-isolated property 'value' cannot cross actor boundary
        }
    }

Also, got this weirdness with add method if I remove @FooActor task:

    func add() async {
        await self.someVar?.protectedStorage.append("test") // Global actor 'FooActor'-isolated property 'protectedStorage' can not be mutated on a non-isolated actor instance
                                                            // No 'async' operations occur within 'await' expression
    }

I want to do everything right to ensure my code is swift 6 ready.
How can I work with one actor isolated values from another actor context with no compiler errors, no boilerplate, thread safe and compact enough?
Why so many errors on all approaches I try (no matter what I try).
What is the right way to store another actor-isolated variables within another actor?

First of all, I have to mention that mixing isolation domains inside one type not a good idea. It is really hard to get right, and no wonder why you end up fighting compiler.

That part is really easy to fix: isolate init on @MainActor.

And that you can fix by adding Sendable requirement on a protocol.


But I really advise against such way of mixing isolations, rather then have clear distinction and communicate between actors by "messages" – which is function calls in Swift — as this is the way Actors model have by design. Because even with errors fixed, you'll run into new one again and again, fighting compiler for no good reason.

I totally agree, that different isolation domains within an actor is a bad idea. I could remove global actor mark from the variables, if only the compiler allow me to do it later in code.
As you may see in the second code snippet, the compiler simply doesn't allow me to assign a variable initial value, marking a code as risking a data race.
I also cannot mark an init with MainActor, since the real init I'm troubling with sets a different actor with different isolation context, so despite compiler is now allow me to set MainActor variable initial state, it doesn't for another variable ( cannot be mutated / self captured by a closure)

So I completely stuck on resolving this :frowning: