Reputation: 3964
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
Reputation: 7361
Couple of changes are necessary:
Self.AllCases.Index?
rather than Int?
. In practice, these types will be equivalent, as seen below.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.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