From my previous post asked why there are four versions of String.init(describing:) when the most general one can suffice, learned a revelation to me:
Value of protocol type 'CustomStringConvertible' cannot conform to 'CustomStringConvertible'; only struct/enum/class types can conform to protocols
so because of above, this:
String.init(describing: Foo() as CustomStringConvertible)
does not match to the <Subject: CustomStringConvertible>
version, it match to the most general <Subject>
version.
but I find that it can be written this way instead:
String.init(describing instance: CustomStringConvertible) { ... }
and when written this way, it would call the matching version and do not have the "Value of protocol type 'CustomStringConvertible' cannot conform to 'CustomStringConvertible'" problem.
So, why are the String.init(describing:)
written with generic? Why not just protocol type as parameter type like above? So passing protocol parameter calls the specific version.
With the way String.init(describing:)
's are written now, when you only have a protocol type value, it will call the most general and cannot call the specific one.
My playground:
//: [Previous](@previous)
extension String {
// declaring this way with generic, protocol parameter match to the 4th call
init<Subject: TextOutputStreamable>(xxx instance: Subject) {
print("xxx11111")
self.init(describing: instance)
}
init<Subject: CustomStringConvertible>(xxx instance: Subject) {
print("xxx2222")
self.init(describing: instance)
}
init<Subject: CustomStringConvertible & TextOutputStreamable>(xxx instance: Subject) {
print("xxx3333")
self.init(describing: instance)
}
init<Subject>(xxx instance: Subject) {
print("xxx4444")
self.init(describing: instance)
}
// but declaring this way, protocol is match to exact protocol param type call!
init(yyy instance: TextOutputStreamable) {
print("yyy11111")
self.init(describing: instance)
}
init(yyy instance: CustomStringConvertible) {
print("yyy2222")
self.init(describing: instance)
}
init(yyy instance: CustomStringConvertible & TextOutputStreamable) {
print("yyy3333")
self.init(describing: instance)
}
init<Subject>(yyy instance: Subject) {
print("yyy4444")
self.init(describing: instance)
}
}
extension String.StringInterpolation {
mutating func appendInterpolation(_ instance: TextOutputStreamable?, default defaultValue: @autoclosure () -> String) {
print("-a1-")
// this only match to 4th generic one
appendLiteral(instance.map(String.init(xxx:)) ?? defaultValue())
// but this match to specific one
appendLiteral(instance.map(String.init(yyy:)) ?? defaultValue())
}
mutating func appendInterpolation(_ instance: CustomStringConvertible?, default defaultValue: @autoclosure () -> String) {
print("-a2-")
appendLiteral(instance.map(String.init(xxx:)) ?? defaultValue())
appendLiteral(instance.map(String.init(yyy:)) ?? defaultValue())
}
mutating func appendInterpolation(_ instance: (CustomStringConvertible & TextOutputStreamable)?, default defaultValue: @autoclosure () -> String) {
print("-a3-")
appendLiteral(instance.map(String.init(xxx:)) ?? defaultValue())
appendLiteral(instance.map(String.init(yyy:)) ?? defaultValue())
}
mutating func appendInterpolation<Subject>(_ instance: Subject?, default defaultValue: @autoclosure () -> String) {
print("-a4-")
appendLiteral(instance.map(String.init(xxx:)) ?? defaultValue())
appendLiteral(instance.map(String.init(yyy:)) ?? defaultValue())
}
}
struct Foo: CustomStringConvertible, TextOutputStreamable {
func write<Target>(to target: inout Target) where Target : TextOutputStream {
target.write("Text Output Streamable")
}
var description: String { "Custom String Convertible" }
}
print("Interpolation", "let s1 = Foo() as CustomStringConvertible")
let s1 = Foo() as CustomStringConvertible
print("\(s1, default: "s1 is nil")")
// print out:
//-a2-
//xxx4444
//yyy2222
//Text Output StreamableText Output Streamable
print("Interpolation", "let s2 = Foo()")
let s2 = Foo()
print("\(s2, default: "s2 is nil")")
//print out:
//Interpolation let s2 = Foo()
//-a3-
//xxx4444
//yyy3333
//Text Output StreamableText Output Streamable
//: [Next](@next)