Swift: 调用枚举值转换器函数作为第一个 class 函数

Swift: Calling an Enum value converter function as a first class function

enum SolarSystemPlanet: String, CaseIterable {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune

    func toRawValue(_ value: SolarSystemPlanet) -> PlanetName {
        value.rawValue
    }
}

使用上面的枚举,获取行星名称数组的一种方法是调用

SolarSystemPlanet.allCases.map { [=13=].rawValue }

但是 Swift 支持 first-class 函数,将函数视为 "first class citizens",这使我们可以像调用任何其他对象或值一样调用函数。

所以通过这个得到一个名字数组会很好

SolarSystemPlanet.allCases.map(.toRawValue)

但是,编译器似乎需要更多上下文。它无法在编译时推断 map 中的类型,所以我做了

SolarSystemPlanet.allCases.map(SolarSystemPlanet.toRawValue)

编译器不再报错,但我没有得到字符串数组。 return 上面的行是一个 [(SolarSystemPlanet) -> String]

类型的值

如果我把上面的打印出来,而不是得到

["mercury", "venus", "earth", "mars", "jupiter", "saturn", "uranus", "neptune"]

我得到了

[(Function), (Function), (Function), (Function), (Function), (Function), (Function), (Function)]

如果我强制 return 类型为 [String] 像这样

var planets: [String] = SolarSystemPlanet.allCases.map(SolarSystemPlanet.toRawValue)

Xcode 会抱怨 [(SolarSystemPlanet) -> String] 无法转换为 [String]

到底有没有可能实现我想要做的事情?我是漏了什么还是做错了什么?

如果不可能,我也非常感谢对原因的一些解释。

感谢您花时间阅读我的问题!


编辑 感谢 @sweeper 的回答。

对于那些感兴趣的人,我稍微进一步确保每个字符串枚举都有 toRawValue

extension RawRepresentable where RawValue == String {
    static func toRawValue(_ value: Self) -> PlanetName {
        value.rawValue
    }
}

注:这是Swift5.1.3

请注意 toRawValue 不需要是实例方法。可以是静态的:

static func toRawValue(_ value: SolarSystemPlanet) -> PlanetName {
    value.rawValue
}

现在您可以使用 SolarSystemPlanet.toRawValue 作为 map 的参数。

或者,在这种情况下,您还可以使用 \.rawValuekeypath 作为 map 的参数:

SolarSystemPlanet.allCases.map(\.rawValue)

这是 Swift 5.2 的新功能。

编辑:实例方法为何不起作用的解释

在 Swift 中,当从静态上下文访问时,具有签名 (U) -> R 的类型 T 上的实例方法变为具有签名 [=20= 的静态方法].实例方法需要一个封闭类型的实例来调用,对吗?因此,当您将 T 传递给它时,它会返回原始实例函数 (U) -> R

因此,非静态SolarSystemPlanet.toRawValue的类型是

(SolarSystemPlanet) -> ((SolarSystemPlanet) -> String)

这解释了为什么在应用 map 之后,数组变成了 [(SolarSystemPlanet) -> String]