here is a protocol Indexable, with an associated type requirement Index. it gets a default “implementation” for Index where Self: RawRepresentable, and has a refining protocol IndexableString that provides another default “implementation” for Index where Self: LosslessStringConvertible.
protocol Indexable {
associatedtype Index
}
extension Indexable where Self: RawRepresentable, RawValue: Indexable {
typealias Index = RawValue.Index
}
protocol IndexableString: Indexable where Index == String.Index {}
extension IndexableString where Self: LosslessStringConvertible {
typealias Index = String.Index
}
extension Int8: Indexable {
typealias Index = Int
}
enum E: Int8 {
case foobie
case barbie
}
extension E: Indexable {}
this means if a type is both RawRepresentable and LosslessStringConvertible, there will be two witness candidates, the one from Indexable and the one from IndexableString.
if Index were a function, subscript, or property requirement, this would Just Work, because every IndexableString type is also Indexable, so the more-specific overload is chosen. but for some reason typealiases do not benefit from this disambiguation logic.
enum F: Int8 {
case foobie
case barbie
}
extension F: IndexableString {}
// Type 'F' does not conform to protocol 'Indexable'
// Multiple matching types named 'Index'
extension F: LosslessStringConvertible {
var description: String { "" }
init?(_: String) { return nil }
}
this is not good! we want to use the type requirement here, as a way of “bundling” a bunch of static members we do not want polluting the namespace of the conforming type. similarly, we want the RawRepresentable shortcut to just “be there”, we don’t want to introduce a third protocol like IndexableByRawRepresentation just to opt into that.