Why are protocols annotated with @MainActor vs only their requirements?

@MainActor protocol ImAProtocol {
    func doThing(with str: String)
    func doAnotherThing()
}

Why do we allow annotation of an entire protocol as @MainActor vs their requirements individually? I can't think of why the isolation of the entire type implementing the protocol is relevant at all, as long as we call the requirements on the correct actor. I'd think we can still pass the implementing type between actors without issue until we're actually calling it.

protocol ImAProtocol {
    @MainActor func doThing(with str: String)
    @SomeOtherGlobalActor func doAnotherThing()
}
1 Like

bump, just want someone to double check if my understanding of concurrency here is correct

Calling the requirements is one aspect of the protocol, but the isolation of the type can make a difference if you intend to store a value of type, e.g., any IAmAProtocol, and want to ensure it's safe to do so on a specific actor (without necessarily requiring that the type be fully Sendable).

(Though I suspect that in practice, most folks mark entire protocols with @MainActor as a shorthand for marking each of the requirements as @MainActor, and it rarely makes a difference because conforming types are typically already either Sendable or actor-bound)

1 Like

why would storing it be unsafe, if it's already instantiated and isn't called?