cyanide
cyanide

Reputation: 3964

Swift: Index of an element in enum (CaseIterable)

The following code (compiles without errors) retrieves index of an element in a particular CaseIterable enum type

public enum MyEnum : CaseIterable {
    case ONE, TWO, THREE

    public func ordinal() -> Int? {
       return MyEnum.allCases.firstIndex(of: self)
    }
}

I want to make a generic function to work with all CaseIterable enums.

If I try:

 public extension CaseIterable {

    public func ordinal() -> Int? {
        return CaseIterable.allCases.firstIndex(of: self)
    }
 }

I get a compiler error "Member 'allCases' cannot be used on value of protocol type 'CaseIterable'; use a generic constraint instead" which is quite logical, as the actual enum type is unknown".

When I try CaseIterable<T>, I get another error, as CaseIterable is not declared as generic type.

Is there a way?

Upvotes: 7

Views: 5165

Answers (1)

Connor Neville
Connor Neville

Reputation: 7361

Couple of changes are necessary:

  1. The return type needs to be Self.AllCases.Index? rather than Int?. In practice, these types will be equivalent, as seen below.
  2. You also need to constrain any types to Equatable, because you need to be equatable in order to use firstIndex(of:). Again, in practice, any CaseIterable will usually be an enum without associated values, meaning it will be equatable automatically.
  3. (Optional change) This function will never return nil, because you're finding one case in a CaseIterable. So you can remove the optionality on the return type (Self.AllCases.Index) and force unwrap.

Example:

public extension CaseIterable where Self: Equatable {

    public func ordinal() -> Self.AllCases.Index {
        return Self.allCases.firstIndex(of: self)!
    }

}

enum Example: CaseIterable {

    case x
    case y

}

Example.y.ordinal()  // 1
type(of: Example.y.ordinal()) // Int

Personally, I'd add that "Ordinal" usually means something different than what you're doing, and I'd recommend changing the function name to elementIndex() or something. But that's an aside.

Upvotes: 17

Related Questions