What's the best way to override CustomStringConvertible for a collection?

Great, thanks for confirming. :+1:

In retrospect, it does make sense that Swift wouldn't want you to change behavior built into the standard library. (Or, as @jrose said, Swift is a Safe, Fast, and Expressive language, and "Safe" is paramount.) I guess that protocol extensions are just so convenient and powerful that I get a bit carried away sometimes. :smile:

All that said, I'm still not 100% sure I understand why, given this extension:

extension Set where Element == Int {
    var description: String {
        let values = self.map({ "\($0)" }).joined(separator: ", ")
        return "{\(values)}"
    }
}

these two statements don't produce the same result:

String(describing: set) // prints "[7, 3, 15, 31]"
set.description // prints "{7, 3, 15, 31}"

Why does the former use the default implementation in the standard library but the latter uses the custom implementation in our extension? Is it just because init(describing:) takes a CustomStringConvertible argument but description is being called on a Set<Int>?

Ultimately, I think that the best route to take in this case would probably be to extend DefaultStringInterpolation and add an appendInterpolation(_:) method:

extension DefaultStringInterpolation {
    mutating func appendInterpolation(_ set: Set<Int>) {
        appendLiteral("{")
        appendLiteral(set.map({ "\($0)" }).joined(separator: ", "))
        appendLiteral("}")
    }
}

This provides the aesthetically pleasing syntax that I was trying to achieve at the call site in the first place:

"\(set)" // prints "{7, 3, 15, 31}"

I think that "a fair amount of boilerplate" may be a bit of an understatement for a custom struct that wraps Set. :laughing:

For the sake of argument, though, let's suppose that I wanted to go to the trouble of creating my own custom wrapper type around Set (however ill-advised that may be). How would I go about creating this new type? For example, what would be the easiest way to get a list of all the methods, properties, and protocol conformances that I would have to implement in order to make my wrapper as interchangeable with an ordinary Set as possible?

Indeed. You know, that did occur to me in the hours after I published my original post, but I neglected to edit it later to clarify that point… :sweat_smile: