Conforming an actor to Sequence protocol

Viewed 2415

How can one conform an actor to the Sequence protocol? The following code generates the compiler warning:

Instance method 'makeIterator()' isolated to global actor 'MainActor' can not satisfy corresponding requirement from protocol 'Sequence'

@MainActor class Test: Sequence {
    private var contents: [Int] = []
    
    func makeIterator() -> Array<Int>.Iterator {
        contents.makeIterator()
    }
}
1 Answers

Applying a global actor, like @MainActor, to a type declaration adds an implicit @MainActor in that type's properties and methods.

This means that your example is equivalent to:

class Test: Sequence {
    @MainActor private var contents: [Int] = []
    
    @MainActor 
    func makeIterator() -> Array<Int>.Iterator {
        contents.makeIterator()
    }
}

This is an issue because Sequence has a makeIterator requirement that is not isolated to a specific global actor. If this was the case you wouldn't be able to use a for-loop on an Array in a background thread. So, to opt-out of the implicit isolation, you can write:

@MainActor class Test: Sequence {
    private var contents: [Int] = []
    
    nonisolated func makeIterator() -> Array<Int>.Iterator {
        // You can't use the isolated property 'contents'
        // here, because it's isolated
    }
}

Perhaps you intend to make Test conform to a Sequence-like protocol whose requirements are isolated to @MainActor. Unfortunately, a similar feature has only been proposed as a future direction.

If you are still confused, it's worth taking a step back and thinking about what you really want to achieve. Actor isolation offers a way to safely mutate data shared across threads. Thus, no isolation implies either a lack of mutation (hence the ability to mark let properties nonisolated) or a synchronization mechanism other than actors.

Related