0

I am a beginner Swift learner and I have a question about protocols. I have followed a tutorial that teaches you about linked lists, which is as follows:

Node:

class LinkedListNode<Key> {
    let key: Key
    var next: LinkedListNode?
    weak var previous: LinkedListNode?

    init (key: Key) {
        self.key = key
    }
}

And the linked list:

class LinkedList<Element>: CustomStringConvertible {
    typealias Node = LinkedListNode<Element>

    private var head: Node?

    // irrelevant code removed here

    var description: String {
        var output = "["
        var node = head
        while node != nil {
            output += "\(node!.key)"
            node = node!.next
            if node != nil { output += ", " }
        }
        return output + "]"
    }
}

The var description: String implementation simply lets you to print each elements in the linked list.

So far, I understand the structure of the linked list, my problem isn't about the linked list actually. What I don't understand is the protocol CustomStringConvertible. Why would it be wrong if I have only the var description: String implementation without conforming to the protocol? I mean, this protocol just simply say "Hey, you need to implement var description: String because you are conformed to me, but why can't we just implement var description: String without conforming to the protocol?

Is it because in the background, there is a function or some sort that takes in a type CustomStringConvertible and run it through some code and voila! text appears.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Brendon Cheung
  • 995
  • 9
  • 29
  • I'm not sure I entirely understand your question – are you asking why types don't implicitly conform to protocols if they just happen to implement all of their requirements? There are good reasons why this isn't the case, as most protocols have specific semantic requirements. For example, the `Error` protocol has *no* requirements at all – but that *doesn't* mean that all types are errors. – Hamish May 02 '17 at 15:07
  • See [Protocols are more than Bags of Syntax](https://oleb.net/blog/2016/12/protocols-have-semantics/) – Hamish May 02 '17 at 15:13

3 Answers3

2

Why can't we just implement var description: String without conforming to the protocol?

Compare:

class Foo {
    var description: String { return "my awesome description" }
}

let foo = Foo()

print("\(foo)") // return "stackoverflow.Foo" (myBundleName.Foo)

and

class Foo: CustomStringConvertible {
    var description: String { return "my awesome description" }
}

let foo = Foo()

print("\(foo)") // return "my awesome description"

When you use CustomStringConvertible, you warrant that this class have the variable description, then, you can call it, without knowing the others details of implementation.

Another example:

(someObject as? CustomStringConvertible).description

I don't know the type of someObject, but, if it subscriber the CustomStringConvertible, then, I can call description.

macabeus
  • 4,156
  • 5
  • 37
  • 66
1

You must conform to CustomStringConvertible if you want string interpolation to use your description property.

You use string interpolation in Swift like this:

"Here's my linked list: \(linkedList)"

The compiler basically turns that into this:

String(stringInterpolation:
    String(stringInterpolationSegment: "Here's my linked list: "),
    String(stringInterpolationSegment: linkedList),
    String(stringInterpolationSegment: ""))

There's a generic version of String(stringInterpolationSegment:) defined like this:

public init<T>(stringInterpolationSegment expr: T) {
  self = String(describing: expr)
}

String(describing: ) is defined like this:

public init<Subject>(describing instance: Subject) {
  self.init()
  _print_unlocked(instance, &self)
}

_print_unlocked is defined like this:

internal func _print_unlocked<T, TargetStream : TextOutputStream>(
  _ value: T, _ target: inout TargetStream
) {
  // 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(type(of: value)) {
    let debugPrintable = value as! CustomDebugStringConvertible
    debugPrintable.debugDescription.write(to: &target)
    return
  }
  if case let streamableObject as TextOutputStreamable = value {
    streamableObject.write(to: &target)
    return
  }

  if case let printableObject as CustomStringConvertible = value {
    printableObject.description.write(to: &target)
    return
  }

  if case let debugPrintableObject as CustomDebugStringConvertible = value {
    debugPrintableObject.debugDescription.write(to: &target)
    return
  }

  let mirror = Mirror(reflecting: value)
  _adHocPrint_unlocked(value, mirror, &target, isDebugPrint: false)
}

Notice that _print_unlocked only calls the object's description method if the object conforms to CustomStringConvertible.

If your object doesn't conform to CustomStringConvertible or one of the other protocols used in _print_unlocked, then _print_unlocked creates a Mirror for your object, which ends up just printing the object's type (e.g. MyProject.LinkedList) and nothing else.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
-1

CustomStringConvertible allows you to do a print(linkedListInstance) that will print to the console whatever is returned by the description setter.

You can find more information about this protocol here: https://developer.apple.com/reference/swift/customstringconvertible

Grossi
  • 1
  • 1
  • 1