带有自定义初始值设定项的 Swift 枚举丢失 rawValue 初始值设定项

我试图将这个问题归结为以下最简单的形式。

设置

Xcode Version 6.1.1(6A2008a)

MyEnum.swift中定义的枚举:

internal enum MyEnum: Int {
case Zero = 0, One, Two
}


extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero": self = .Zero
case "one": self = .One
case "two": self = .Two
default: return nil
}
}
}

以及在另一个文件 MyClass.swift中初始化枚举的代码:

internal class MyClass {
let foo = MyEnum(rawValue: 0)  // Error
let fooStr = MyEnum(string: "zero")


func testFunc() {
let bar = MyEnum(rawValue: 1)  // Error
let barStr = MyEnum(string: "one")
}
}

错误

当试图用原始值初始值设定项初始化 MyEnum时,Xcode 给出了以下错误:

Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?'

笔记

  1. 每间 快速语言指南:

    如果使用原始值类型定义枚举,枚举将自动接收初始值设定项,该初始值设定项接受原始值类型的值(作为名为 rawValue的参数) ,并返回枚举成员或 nil

  2. MyEnum的自定义初始值设定项是在一个扩展中定义的,用于测试枚举的原始值初始值设定项是否因为 语文指引的以下情况而被删除。但是,它得到了相同的错误结果。

    请注意,如果为值类型定义自定义初始值设定项,则不再能够访问该类型的默认初始值设定项(或成员方式的初始值设定项,如果它是一个结构)。[...]
    如果您希望您的自定义值类型可以使用默认初始值设定项和成员初始值设定项以及您自己的自定义初始值设定项进行初始化,请将您的自定义初始值设定项写入扩展中,而不是作为值类型的原始实现的一部分。

  3. 将枚举定义移动到 MyClass.swift可以解决 bar的错误,但是 foo不会。

  4. 删除自定义初始值设定项可以解决这两个错误。

  5. 一种解决办法是在枚举定义中包含以下函数,并使用它来代替提供的原始值初始值设定项。因此,似乎添加自定义初始值设定项与标记原始值初始值设定项 private具有类似的效果。

    init?(raw: Int) {
    self.init(rawValue: raw)
    }
    
  6. Explicitly declaring protocol conformance to RawRepresentable in MyClass.swift resolves the inline error for bar, but results in a linker error about duplicate symbols (because raw-value type enums implicitly conform to RawRepresentable).

    extension MyEnum: RawRepresentable {}
    

Can anyone provide a little more insight into what's going on here? Why isn't the raw-value initializer accessible?

67643 次浏览

Add this to your code:

extension MyEnum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .Zero
case 1: self = .One
case 2: self = .Two
default: return nil
}
}
}
extension TemplateSlotType {
init?(rawString: String) {
// Check if string contains 'carrousel'
if rawString.rangeOfString("carrousel") != nil {
self.init(rawValue:"carrousel")
} else {
self.init(rawValue:rawString)
}
}
}

In your case this would result in the following extension:

extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero":
self.init(rawValue:0)
case "one":
self.init(rawValue:1)
case "two":
self.init(rawValue:2)
default:
return nil
}
}
}

Yeah this is an annoying issue. I'm currently working around it using a global-scope function that acts as a factory, i.e.

func enumFromString(string:String) -> MyEnum? {
switch string {
case "One" : MyEnum(rawValue:1)
case "Two" : MyEnum(rawValue:2)
case "Three" : MyEnum(rawValue:3)
default : return nil
}
}

This bug is solved in Xcode 7 and Swift 2

You can even make the code simpler and useful without switch cases, this way you don't need to add more cases when you add a new type.

enum VehicleType: Int, CustomStringConvertible {
case car = 4
case moped = 2
case truck = 16
case unknown = -1


// MARK: - Helpers


public var description: String {
switch self {
case .car: return "Car"
case .truck: return "Truck"
case .moped: return "Moped"
case .unknown: return "unknown"
}
}


static let all: [VehicleType] = [car, moped, truck]


init?(rawDescription: String) {
guard let type = VehicleType.all.first(where: { description == rawDescription })
else { return nil }
self = type
}
}

This works for Swift 4 on Xcode 9.2 together with my EnumSequence:

enum Word: Int, EnumSequenceElement, CustomStringConvertible {
case apple, cat, fun


var description: String {
switch self {
case .apple:
return "Apple"
case .cat:
return "Cat"
case .fun:
return "Fun"
}
}
}


let Words: [String: Word] = [
"A": .apple,
"C": .cat,
"F": .fun
]


extension Word {
var letter: String? {
return Words.first(where: { (_, word) -> Bool in
word == self
})?.key
}


init?(_ letter: String) {
if let word = Words[letter] {
self = word
} else {
return nil
}
}
}


for word in EnumSequence<Word>() {
if let letter = word.letter, let lhs = Word(letter), let rhs = Word(letter), lhs == rhs {
print("\(letter) for \(word)")
}
}

Output

A for Apple
C for Cat
F for Fun