Wildchild9
(Wildchild9)
1
Conditional Conformance Problems
I was writing code earlier to make Array conform to CustomPlaygroundDisplayConvertible when it's elements are of type CGPoint. I wanted to make a different conformance for an Array when its elements are of type Optional<CGPoint> and ran into the following problem:
extension Array: CustomPlaygroundDisplayConvertible where Element == Optional<CGPoint> {
//...
}
// Error: Conflicting conformance of 'Array<Element>' to protocol 'CustomPlaygroundDisplayConvertible'; there cannot be more than one conformance, even with different conditional bounds
extension Array: CustomPlaygroundDisplayConvertible where Element == CGPoint {
//...
}
I haven't a clue why this is happening, aren't these different types?
Hello,
Multiple conditional conformances are currently not allowed: swift-evolution/0143-conditional-conformances.md at master · apple/swift-evolution · GitHub
In your case it looks innocuous, but there are many possible traps, as explained in the previous link. See also those related threads, for more context and information:
twof
3
You may be able to get the result you're looking for by doing something like this.
extension Array: CustomPlaygroundDisplayConvertible { }
extension Array where Element == Optional<CGPoint> {
// CustomPlaygroundDisplayConvertible implementation goes here
}
extension Array where Element == CGPoint {
// CustomPlaygroundDisplayConvertible implementation goes here
}
extension Array {
// Default CustomPlaygroundDisplayConvertible implementation goes here
}
1 Like
SDGGiesbrecht
(Jeremy David Giesbrecht)
4
If you do that, Array’s conformance will be with the default method. The others will only ever be called if they happen to be an available static overload in a particular context. Namely:
let point = CGPoint(x: 1, y: 2)
// This will use the more‐specific overload.
print(array.playgroundDescription)
func demonstrate<T>(_ array: [T]) {
// But this will only ever use the default implementation...
print(array.playgroundDescription)
}
demonstrate(CGPoint(point)) // ...even when you probably want the specific one.
Instead, try to design it like this:
protocol CustomArrayElementPlaygroundDisplayConvertible {
// Declare whatever options you’ll use here.
var somethingRelevant: String { get }
}
extension Array where Element : CustomArrayElementPlaygroundDisplayConvertible {
var playgroundDescription: Any {
// Construct something using CustomArrayElementPlaygroundDisplayConvertible.
return map({ $0.somethingRelevant }).joined()
}
}
extension CGPoint : CustomArrayElementPlaygroundDisplayConvertible {
// Conform...
}
extension Optional : CustomArrayElementPlaygroundDisplayConvertible
where Wrapped : CustomArrayElementPlaygroundDisplayConvertible {
// Conform...
}
But you may want to do that on a wrapper instead of directly on Array, because there are pitfalls to creating a conformance of a protocol you do not control to a type you do not control.