In the following Swift code:
import Foundation
struct MySet<T: Hashable>: CustomStringConvertible {
let set: Set<T>
}
extension MySet {
var description: String {
return "\(self.set)"
}
}
extension MySet where T: Comparable {
var description: String {
return "\(self.set.sorted())"
}
}
let intSet: Set<Int> = [6, 3, 4, 9, 12, 49, -1, 44, -1000, 1000]
let myIntSet = MySet(set: intSet)
print("myIntSet: \(myIntSet.description)")
print("myIntSet: \(myIntSet)")
print("myIntSet: ", String(describing: myIntSet))
I'm trying to create a custom description (for the CustomStringConvertible protocol) that is different depending on whether the elements of the set are Comparable. The output of the above is:
myIntSet: [-1000, -1, 3, 4, 6, 9, 12, 44, 49, 1000]
myIntSet: [9, -1, 3, 1000, 6, 4, 12, 44, 49, -1000]
muIntSet: [9, -1, 3, 1000, 6, 4, 12, 44, 49, -1000]
As you can see, if I explicitly print myIntSet.description
, I get the desired output, which is a sorted list. But if I use string interpolation on the struct itself, or String(describing: myIntSet)
, I get the description from the first (unconditional) extension.
Can anyone explain why this is happening? How do I get string interpolation and String(describing: ...)
to use the desired description?
Every protocol requirement can only have one witness. In this case the witness for CustomStringConvertible.description
is the one declared in the first MySet
extension. The description
declared in the second extension only hides the description
declared in the first extension, and is not a witness to the protocol requirement.
When you access description
on a comparable MySet
, you cannot access the hidden implementation, but when description
is accessed through the CustomStringConvertible
protocol (indirectly by string interpolation or String.init(describing:)
), only the protocol witness can be accessed.
This means that all the behaviour you want must be put in the description
declared in the first extension. For example, you can add your own protocol that allows you to provide a custom description, then check if self
conforms to that protocol in description
.
protocol CustomSetDescribable {
var customDescription: String { get }
}
struct MySet<T: Hashable>: CustomStringConvertible {
let set: Set<T>
}
extension MySet {
var description: String {
(self as? any CustomSetDescribable)?.customDescription ?? "\(self.set)"
}
}
extension MySet: CustomSetDescribable where T: Comparable {
var customDescription: String { "\(set.sorted())" }
}