Why does the print function respect a CustomStringConvertible extension on Optional?


(Ian Terrell) #1

I sent this to swift-users a few days ago but no one bit. I figure maybe
someone here might have some insight into the internals for why this is
happening. :slight_smile:

A coworker had the idea to get rid of the "Optional("Bob")" default string
value for Optional<String> by adding the following extension to Optional:

extension Optional: CustomStringConvertible {
    public var description: String {
        switch self {
        case .Some(let wrapped): return "\(wrapped)"
        case .None: return "nil"
        }
    }
}

But this shows the following behavior:

name.description
=> "Bob"

String.init(name)
=> "Optional(\"Bob\")"

String(name) == name.description
=> false

Ok. So I dug around the source and found that String.init<T> delegates
to _print_unlocked [0], where Optional is special cased at the top:

// Optional has no representation suitable for display; therefore,
// values of optional type should be printed as a debug
// string. Check for Optional first, before checking protocol
// conformance below, because an Optional value is convertible to a
// protocol if its wrapped type conforms to that protocol.
if _isOptional(value.dynamicType) {
  let debugPrintable = value as! CustomDebugStringConvertible
  debugPrintable.debugDescription.write(to: &target)
  return
}

This seems non-ideal to me, but I understand that "because an Optional
value is convertible to a protocol if its wrapped type conforms to that
protocol" there's no easy way to tell if the Optional type itself conforms
to CustomStringConvertible.

The print function is documented as using String.init<T> and in the source
it appears to also be delegating to _print_unlocked. However, when I use
it, I see the extension provided description!

print(name)
// => "Bob"

I've also confirmed it's actually the extension and not any other issue by
returning hardcoded values from the description method.

So: what gives? :slight_smile:

Why does the print function respect a CustomStringConvertible extension on
Optional?

Thanks!
Ian

[0]:
https://github.com/apple/swift/blob/master/stdlib/public/core/OutputStream.swift#L177