Get Rawvalue from Enum in a Generic Function

Get rawValue from enum in a generic function

Unfortunately, this doesn't look like it's possible in Swift at this point, but I've thought about your problem for a while, and I'll propose 3 ways that the Swift team could enable you to solve this problem.

  1. Fix the mirror for enums. The most straightforward solution is one that I'm sure you already tried. You're trying to build a reflection library, and you'd like to reflect an Any value to see if it's an enum, and if it is, you'd like to see if it has a raw value. The rawValue property should be accessible via this code:

    let mirror = reflect(theEnum) // theEnum is of Any type
    for i in 0..<mirror.count {
    if mirror[i].0 == "rawValue" {
    switch mirror[i].1.value {
    case let s as String:
    return s
    case let csc as CustomStringConvertible:
    return csc.description
    default:
    return nil
    }
    }
    }

However, this doesn't work. You'll find that the mirror has a count of 0. I really think that this is an oversight on the part of the Swift team in their implementation of Swift._EnumMirror, and I'll be filing a radar about this. rawValue is definitely a legitimate property. It is a weird scenario because enums aren't allowed to have other stored properties. Also, your enum's declaration never explicitly conforms to RawRepresentable, nor does it declare the rawValue property. The compiler just infers that when you type enum MyEnum: String or : Int or whatever. In my tests, it appears that it shouldn't matter whether the property is defined in a protocol or is an instance of an associated type, it should still be a property represented in the mirror.


  1. Allow for protocol types with defined associated types. As I mentioned in my comment above, it's a limitation in Swift that you cannot cast to a protocol type that has associated type requirements. You can't simply cast to RawRepresentable because Swift doesn't know what type the rawValue property will return. Syntax such as RawRepresentable<where RawValue == String> is conceivable, or perhaps protocol<RawRepresentable where RawValue == String>. If this were possible, you could try to cast to the type through a switch statement as in:

    switch theEnum {
    case let rawEnum as protocol<RawRepresentable where RawValue == String>:
    return rawEnum.rawValue
    // And so on
    }

But that's not defined in Swift. And if you just try to cast to RawRepresentable, the Swift compiler tells you that you can only use this in a generic function, but as I look at your code, that's only led you down a rabbit-hole. Generic functions need type information at compile-time in order to work, and that's exactly what you don't have working with Any instances.

The Swift team could change protocols to be more like generic classes and structs. For example MyGenericStruct<MyType> and MyGenericClass<MyType> are legitimately specialized concrete types that you can make a runtime check on and cast to. However, the Swift team may have good reasons for not wanting to do this with protocols. Specialized versions of protocols (i.e. protocol references with known associated types) still wouldn't be concrete types. I wouldn't hold my breath for this ability. I consider this the weakest of my proposed solutions.


  1. Extend protocols to conform to protocols. I really thought I could make this solution work for you, but alas no. Since Swift's built-in mirror for enums doesn't deliver on the rawValue, I thought why not implement my own generic mirror:

    struct RawRepresentableMirror<T: RawRepresentable>: MirrorType {
    private let realValue: T

    init(_ value: T) {
    realValue = value
    }

    var value: Any { return realValue }
    var valueType: Any.Type { return T.self }
    var objectIdentifier: ObjectIdentifier? { return nil }
    var disposition: MirrorDisposition { return .Enum }
    var count: Int { return 1 }

    subscript(index: Int) -> (String, MirrorType) {
    switch index {
    case 0:
    return ("rawValue", reflect(realValue.rawValue))
    default:
    fatalError("Index out of range")
    }
    }

    var summary: String {
    return "Raw Representable Enum: \(realValue)"
    }

    var quickLookObject: QuickLookObject? {
    return QuickLookObject.Text(summary)
    }
    }

Great! Now all we have to do is extend RawRepresentable to be Reflectable so that we can first cast theEnum as Reflectable (no associated type required) and then call reflect(theEnum) to give us our awesome custom mirror:

    extension RawRepresentable: Reflectable {
func getMirror() -> MirrorType {
return RawRepresentableMirror(self)
}
}

Compiler error: Extension of protocol 'RawRepresentable' cannot have
an inheritance clause

Whaaaat?! We can extend concrete types to implement new protocols, such as:

    extension MyClass: MyProtocol {
// Conform to new protocol
}

As of Swift 2, we can extend protocols to give concrete implementations of functions, such as:

    extension MyProtocol {
// Default implementations for MyProtocol
}

I thought for sure we could extend protocols to implement other protocols, but apparently not! I see no reason why we couldn't have Swift do this. I think this would very much fit in with the protocol-oriented programming paradigm that was the talk of WWDC 2015. Concrete types that implement the original protocol would get a new protocol conformance for free, but the concrete type is also free to define its own versions of the new protocol's methods. I'll definitely be filing an enhancement request for this because I think it could be a powerful feature. In fact, I think it could be very useful in your reflection library.

Edit: Thinking back on my dissatisfaction with answer 2, I think there's a more elegant and realistic possibility for working with these protocols broadly, and that actually combines my idea from answer 3 about extending protocols to conform to new protocols. The idea is to have protocols with associated types conforming to new protocols that retrieve type-erased properties of the original. Here is an example:

protocol AnyRawRepresentable {
var anyRawValue: Any { get }
}

extension RawRepresentable: AnyRawRepresentable {
var anyRawValue: Any {
return rawValue
}
}

Extending the protocol in this way wouldn't be extending inheritance per se. Rather it would be just saying to the compiler "Wherever there is a type that conforms to RawRepresentable, make that type also conform to AnyRawRepresentable with this default implementation." AnyRawRepresentable wouldn't have associated type requirements, but can still retrieve rawValue as an Any. In our code, then:

if let anyRawEnum = theEnum as? AnyRawRepresentable {  // Able to cast to
let anyRawValue = anyRawEnum.anyRawValue // anyRawValue is of type Any
switch anyRawValue {
case let s as String:
return s
case let csc as CustomStringConvertible:
return csc.description
default:
return nil
}
}

This kind of solution could be used broadly with any kind of protocol with associated types. I will likewise be including this idea in my proposal to the Swift team for extending protocols with protocol conformance.

Update: None of the above options are available as of Swift 4. I did not receive a response as to why the Mirror on a RawRepresentable enum does not contain its rawValue. As for options #2 and #3, they are still within the realm of possibility for future releases of Swift. They have been mentioned on the Swift mailing list and in the Generics Manifesto document authored by Doug Gregor of the Swift team.

The proper term for option #2 ("Allow for protocol types with defined associated types") is generalized existentials. This would allow protocols with associated types to possibly automatically return Any where there is an associated type or allow for syntax like the following:

anyEnum as? Any<RawRepresentable where .RawValue == String>

Option #3 has been mentioned occasionally on the mailing list, and it is a commonly rejected requested feature, but that is not to say it couldn't be included in future versions of Swift. In the Generics Manifesto, it is termed "Conditional conformances via protocol extensions". While recognizing its power as a feature, it sadly also states that efficient implementation is "nearly impossible."

Passing various enum case as function param in Swift

The linked question is actually quite close - you just have to change the parameter type to just T, instead of T.Type:

func printCase<T : RawRepresentable>(_ e: T) where T.RawValue == String {
print(e.rawValue)
}

Since you are accepting an instance of the enum, not the enum type itself.

Of course, you don't have to restrict this to enums where T.RawValue == String, since print can print Anything.

How can I create an instance of a generic enum in Swift

As noted, your function needs a return type if you want it to return anything. Since you seem to want to use the function to create a value of the specified enum type, that return type should probably be either E or E?. (You're wrapping init?(rawValue:), which returns an optional because rawValue may not map to one of the enum cases. So you either want to pass the optional through to your caller or have some logic in your function to unwrap it and handle the nil case.)

Your parameter rawValue also needs a real type — T.Type is not a fully qualified type in your declaration. You can get at the raw value type of the enum using the RawValue typealias that the RawRepresentable protocol (which you've already given as a generic constraint) defines.

So, here's your function:

func createEnum<E: RawRepresentable>(rawValue: E.RawValue) -> E? {
return E(rawValue: rawValue)
}

Note that if you try something like this:

enum Foo: Int {
case One = 1
case Two = 2
}
createEnum(1)
createEnum<Foo>(1)

It won't work — the first one doesn't specify which specialization of the generic function to use, and the second doesn't work because Swift doesn't allow manual specialization of generic functions. Instead, you have to set it up so that type inference does its thing:

let f: Foo? = createEnum(1)
someFuncThatTakesAFoo(createEnum(1))

generics type constraining for raw value in enums

If the intention is that the function accepts any enumeration type whose raw value is a string then this can be achieved with

func someFunc1<T: CaseIterable & RawRepresentable>(ofType: T.Type) where T.RawValue == String {
for aCase in T.allCases {
let someString: String = aCase.rawValue
print(someString)
}
}

someFunc1(ofType: SomeEnum.self)
// Hello
// How are you?

How to get enum value of raw type from an enum class and a string in kotlin

Here's a pure Kotlin version:

@Suppress("UNCHECKED_CAST")
fun getEnumValue(enumClass: Class<*>, value: String): Enum<*> {
val enumConstants = enumClass.enumConstants as Array<out Enum<*>>
return enumConstants.first { it.name == value }
}

Note that it's not as efficient as the Java version. java.lang.Enum.valueOf uses a cached data structure while this version needs to create a new array to iterate over. Also this version is O(n) wheras the Java version is O(1) as it uses a dictionary under the hood.

There is an open issue in the Kotlin bug tracker to support the same code as in Java which is scheduled for 1.3.

Here's a really ugly hack to workaround the generic type issue:

private enum class Hack

fun getEnumValue(enumClass: Class<*>, value: String): Enum<*> {
return helper<Hack>(enumClass, value)
}

private fun <T : Enum<T>>helper(enumClass: Class<*>, value: String): Enum<*> {
return java.lang.Enum.valueOf(enumClass as Class<T>, value)
}

A quick test shows that it's working but I wouldn't rely on it.


If the generic type is available, you can use the built-in function enumValueOf (see also http://kotlinlang.org/docs/reference/enum-classes.html#working-with-enum-constants):

enum class Color {
Red, Green, Blue
}

enumValueOf<Color>("Red")

Writing a generic function to get common variable from multiple enums in Swift

First of all, you need to create a CommonEnum protocol with ourVar as one of the requirements like so,

protocol CommonEnum {
associatedtype T
var ourVar: T { get }
}

Now conform the above protocol to MyEnum and YourEnum,

enum MyEnum: String, CaseIterable, CommonEnum {
//....
}

enum YourEnum: String,CaseIterable, CommonEnum {
//....
}

Next, the printAll(_:) method will be

func printAll<T>(_ id: T.Type) where T: CommonEnum & RawRepresentable & CaseIterable {
for c in T.allCases {
print(c.rawValue)
print(c.ourVar)
}
}

Example:

printAll(MyEnum.self) //prints a and a1

How to write a generic function to convert any enum value to its string representation?

You need to add a constraint to the 2nd parameter : T[keyof T] instead of number.

Also note that the key of your enum is of type string.

function enumToString<T extends { [name: string]: number | string }>(myenum: T, myenumvalue: T[keyof T]) {
return myenum[myenumvalue];
}

Playground



Related Topics



Leave a reply



Submit