Invalid redeclaration of synthesized implementation for protocol requirement 'allCases'

I've found a very annoying bug in swift 5.4. Consider the following:

enum Scope {
    case ugcImageUpload
    case userReadPlaybackState
    case userModifyPlaybackState
}

extension Scope: CaseIterable {

    static let allCases: Set<Scope> = [  // Invalid redeclaration of synthesized implementation for protocol requirement 'allCases'
        .ugcImageUpload,
        .userReadPlaybackState,
        .userModifyPlaybackState
    ]

}

print(Scope.allCases)  // Error: Ambiguous use of 'allCases'
// presumably because both the synthesized implementation and my implementation exist.

For some reason, I am unable to override the default implementation of allCases for CaseIterable
when the type is Set<Scope>. Interestingly, If I add print(Scope.AllCases.self) and option-click on it, the value of AllCases is [Scope], not Set<Scope>:

If I add an explicit typealias with the correct type for AllCases, then it compiles:

extension Scope: CaseIterable {

    typealias AllCases = Set<Scope>

    static let allCases: Set<Scope> = [
        .ugcImageUpload,
        .userReadPlaybackState,
        .userModifyPlaybackState
    ]

}

Or, If I change

static let allCases: Set<Scope> = [
    .ugcImageUpload,
    .userReadPlaybackState,
    .userModifyPlaybackState
]

to

static let allCases: [Scope] = [
    .ugcImageUpload,
    .userReadPlaybackState,
    .userModifyPlaybackState
]

The it compiles fine.

You can report the bug at bugs.swift.org.

At the same time, I do not really think it is wise to change AllCases into a Set, because that may reduce the performance of iteration, which is the intended use for CaseIterable. I am not really sure why you would need a fast membership check when by definition the result would have to be true anyway. But if some external constraints somehow require it, then I would create a protocol of your own instead of (or inheriting from) CaseIterable.

I just reported the bug there.

That isn't the only benefit of using a set, and it isn't the reason I'm using it here.

The reason that I'm using a set is because all of the other APIs in my library use Set<Scope>. This
prevent duplicates, which don't make sense for this type, allows order order-insensitive comparisons, and allows for mathematical set operations like isSubset.

Are you sure about this?

Yes. Although exactly how much of a difference depends on what you end up doing with it, and it may very well be negligible for your use case.

Array is designed to optimize retrieval of elements in order, or at a particular offset. But by arranging its guts at even intervals in memory to make that fast, it has no quick way to check for membership. contains(_:) must blindly check each entry until it finds a match or has exhaustively checked everything.

Set is the other way around. It is designed to optimize membership checks. But by arranging its guts in a hash table to make that fast, it has to do a little more work digging in buckets to find the location of the next element of the set.

Terms of Service

Privacy Policy

Cookie Policy