你想要一些它做不到的 Swift。特别是,据我所知,Swift 没有联合类型。如果你想要这样的东西,你可能想看看函数式或受函数启发的语言。
插曲:如果这两种类型都是结构或类(即不是协议),您可以使用这种模式:
protocol DataOrError {}
extension Data: DataOrError {}
extension ErrorClass: DataOrError {}
假设没有该协议的其他实现者,您现在有一些接近联合类型的东西。你也可以切换类型:
switch doe {
case let e as Error: print("Error \(e)")
case let d as Data: print("Data \(d)")
default: assert(false, "thou shalt not implement my dummy!")
}
无论如何,我们可以使用具有关联值的枚举来伪造它,这可以(有限制地)用作穷人的联合类型。
像这样定义一个枚举:
enum DataOrError {
case Data(Data)
case Error(Error)
}
现在你可以在任何你想使用的地方使用DataOrError,好吧,拥有Data 或Error 和DataOrError? 中的一个,最多一个。
来电者网站,你会得到这样的东西:
extension String: Error {} // low-effort errors, don't judge me!
func myFunction(completion: (DataOrError) -> ()) {
completion(.Data(Data(bytes: [1,2,3,4,5])))
completion(.Error("this won't work!"))
}
和被调用者站点:
var myCompletion = { (doe: DataOrError) in
switch doe {
case .Data(let d): print("Data \(d)")
case .Error(let e): print("Error \(e)")
}
}
myFunction(completion: myCompletion)
// > Data 5 bytes
// > Error this won't work!
设计说明:您可能正在寻找另一个方向的概括,特别是如果您有许多不同的类型要与Error 进行或运算。在这种情况下,显式包装器可能是一个很好的解决方案,即使您牺牲了良好的语法。
struct TalkativeOptional<T> {
private(set) var rawValue: T?
private(set) var error: Error?
init(value: T) {
self.rawValue = value
}
init(error: Error) {
self.error = error
}
}
请注意这两个属性中的一个究竟可以是非nil。还有两种组合;在这里,您可以通过选择的初始化程序来控制您想要的。
被调用者站点示例:
func convert(_ number: String) -> TalkativeOptional<Int> {
if let int = Int(number) {
return TalkativeOptional(value: int)
} else {
return TalkativeOptional(error: "'\(number)' not a valid integer!")
}
}
调用者站点示例:
var a = convert("dsa2e2")
if let val = a.rawValue {
print("a + 1 = \(val + 1)")
} else { // we know by design that error is set!
print("ERROR: \(a.error!)")
}