How to implement CustomStringConvertible on non-RawRepresentable enum conforming to protocol?

Viewed 140

Consider the following code, which consists of:

  • A protocol, P
  • Two enums, E0 and E1 that conform to P
  • A function, f(p:) that takes an instance of P as an argument
import Foundation

protocol P { }

enum E0: P { case a }

enum E1: P { case b }

func f(p: P) {
    print("\(type(of: p)).\(p)")
}

let e0_a = E0.a
let e1_b = E1.b

f(p: e0_a)  // prints "E0.a"
f(p: e1_b)  // prints "E1.b"

I would like to be able to print a string that describes the p argument in the form:

"Name of type implementing P.case of P"

As shown in the function f(), this works just fine and I get the output I want.

What I would like to do, if possible, is implement a CustomStringConvertible extension on P that generates the same string.

Is this possible?

Here's one approach that doesn't work:

protocol P: CustomStringConvertible { }

extension P {
    var description: String { return "\(type(of: self).\(self)" }
}

This doesn't work because \(self) causes description to recursively call itself until the stack overflows.

Here's another approach that doesn't work:

protocol P: CustomStringConvertible { }

extension P where Self: RawRepresentable {
    var description: String { return "\(type(of: self)).\(self.rawValue)" }
}

This approach doesn't work because E0 isn't RawRepresentable. There is some magic under the hood that allows Swift to print an enum's case name even when the enum has no rawValue, but I'm not sure how to access it. Using something like Mirror(reflecting: p).description prints the type of p, not the enum's case name.

Thank you for any ideas. As I say it can be done in a function that receives a P, but a CustomStringConvertible would be a lot cleaner.

0 Answers
Related