Print rawValue of enum

I'm wondering a little bit about the printing of a enum that has raw values. Why is it, that printing a case prints the name of the case instead of the rawValue?

Example:

enum Test: String {
	case case1 = "abc"
	case case2 = "def"
}

print(Test.case1)

This prints "case1", but not "abc". To get "abc" I need to do a print(Test.case1.rawValue).

But however:

I could to this:

enum Test: String, CustomStringConvertible {
	case case1 = "abc"
	case case2 = "def"
	var description: String {
		self.rawValue
	}
}

But therefore I need to make every enum conform to the CustomStringConvertible where I want to get that behaviour. Unfortunately I have no idea how to create a protocol and/or an extension, that brings me that behaviour... sth. like this:

enum Test: StringEnum {
    case case1 = "abc"
    ...
}

????? StringEnum {
    var description: String {
        self.rawValue
    } 
}

Is this possible?

I guess the best you can do is extending RawRepresentable. You still need to mark your enums with CustomStringConvertible, but at least you don't need to write description:

extension RawRepresentable where RawValue == String {
  var description: String { rawValue }
}

enum Test: String, CustomStringConvertible {
  case case1 = "abc"
  case case2 = "def"
}

print(Test.case1)

Thanks for your answer, but for this I need to mark every String enum as CustomStringConvertible although I don't need it for some enums (for example I have some enum that conforms to CodingKeys).

I would use .rawValue, but I'm iterating over the children of a struct with Mirror(reflecting: ...) and there I only have a Any for the value:

enum TestEnum: String {
	case case1 = "abc"
	case case2 = "def"
}
struct TestStruct {
	var prop1: Int = 5
	var prop2: TestEnum = .case1
}

let test = TestStruct()

let mirror = Mirror(reflecting: test)
mirror.children.forEach { child in
	print("\(child.label) - \(child.value)")   // this prints "case1" instead of "abc"
}

In order to have that custom behavior when printing, you need to conform your type to CustomStringConvertible, so marking every enum with raw values with it is mandatory. There's no way to change protocol inheritance post-definition.

If you are uneasy with the long name of the protocol, you can define a StringEnum protocol which inherits from CustomStringConvertible, but that doesn't change the fact that you will still need to mark your enums with it.

protocol StringEnum: CustomStringConvertible, RawRepresentable where RawValue == String {}
extension StringEnum {
  var description: String { rawValue }
}

enum Test: String, StringEnum {
    case case1 = "abc"
    case case2 = "def"
}

That's (nearly) exactly what I was searching for!! (Would be nice to just write enum Test: StringEnum, but that's totally okay so). Thank you very much for your help.

But still - in general: Why is it like this, that print doesn't print the rawValue?

I suppose it works like this for consistency with the non-RawRepresentable case and because generally the case name is more meaningful than the raw value. It would be less than ideal to have 0, 1, 2 and 3 printed here:

enum TestEnum: UInt8 {
  case up    = 0b00
  case right = 0b01
  case down  = 0b10
  case left  = 0b11
}
2 Likes

rawValues are usually implementation details and rather unimportant. If you care that much about them it seems like your enum cases should just match. But most developers use the ability to set a rawValue to get better naming, so the enum case is more valuable anyway.

1 Like

@xAlien95: I don't agree. I use enum because the code is easier to write and read:

let property = TestEnum.right

instead of

let property = 0b01

Print is convertig a value into a String. The case is just a name for it, not what is really relevant.

@Jon_Shier: The point is, that in my case the raw values are not usable in Swift as they use special characters like ":".

It's a difference between reading and writing code or printing and using the content of a variable. For me it would make more sense to print the raw value instead of the descriptive name of the enum... I also don't expect that print() prints the name of a property instead of the content just because the name is more descriptive. But yes, I know, that there .case1 is different then the rawValue.

But if I am the only one who thinks like this, I may have another suggestion: Why not adding an additional init to the Standard-Library like String(rawValue: RawRepresentable)? Or something else so that it's possible to access the rawValue while iterating over the children in Mirror?