swift - 展开/合并多级可选

标签 swift option-type null-coalescing

我正在尝试编写一个函数来解包具有任意嵌套级别的可选值。这是我正在使用的测试:

let a: Int??? = 1
let b: Int??? = nil
print(a.unwrap(0), b.unwrap(0)) // should print 1, 0

我可以用一个基本的泛型函数得到正确的输出:

extension Optional {
    func unwrap<T> (_ defaultValue: T) -> T {
        return (self as? T) ?? defaultValue
    }
}

print(a.unwrap(0), b.unwrap(0)) // 1, 0

但这并不能阻止使用与可选类型不同的类型调用该函数。例如,我可以调用 a.unwrap("foo"),它会打印“foo”而不是“1”,因为你当然不能转换 Int???String

我尝试使用 Wrapped 代替,它半正确地限制了默认值但没有给出正确的输出:

extension Optional {
    func unwrap (_ defaultValue: Wrapped) -> Wrapped {
        return (self as? Wrapped) ?? defaultValue
    }
}

print(a.unwrap(0), b.unwrap(0)) // Optional(Optional(1)), nil

它只解包可选的一层,而不是所有三层,并且由于 nil 是 Int?? 的有效值,它不会返回默认值。

有什么方法可以安全地做我想做的事吗?

最佳答案

此代码可以满足您的要求。 缺点是您需要在每个要放入可选类型的类型中实现 Unwrappable 协议(protocol)。也许Sourcery可以提供帮助。

protocol Unwrappable {
  associatedtype T

  func unwrap(_ default: T) -> T
}

extension Optional {}

extension Optional: Unwrappable where Wrapped: Unwrappable {
  typealias T = Wrapped.T

  func unwrap(_ defaultValue: T) -> T {
    if let value = self {
      return value.unwrap(defaultValue)
    }
    return defaultValue
  }
}

extension Int: Unwrappable {
  typealias T = Int

  func unwrap(_ default: Int) -> Int {
    return self
  }
}

let nestedOptionalValue: Int??? = 6
let nestedOptionalNil: Int??? = nil
let optionalValue: Int? = 6
let optionalNil: Int? = nil
print(nestedOptionalValue.unwrap(0)) // prints 6
print(nestedOptionalNil.unwrap(0))   // prints 0
print(optionalValue.unwrap(0))       // prints 6
print(optionalNil.unwrap(0))         // prints 0

诀窍在于 Unwrappable 协议(protocol)将最终可以解包(如在 0、1 或更多解包之后)的类型标记为特定类型。

这个问题的难点在于,在对 Optional 的扩展中,你可以获得 Wrapped 类型,但是如果 Wrapped 再次是可选的,你就无法访问 Wrapped.Wrapped(换句话说,Swift 不支持更高种类的类型)。

另一种方法是尝试向 Optional where Wrapped == Optional 添加扩展名.但是同样,您需要 Higher Kinded Types 支持才能访问 Wrapped.Wrapped 类型。如果我们尝试扩展 Optional where Wrapped == Optional<T> ,我们也失败了,因为我们不能在 T 上扩展 Optional。

关于swift - 展开/合并多级可选,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53182664/

相关文章:

swift - UISearchBar 显示键盘

ios - Swift 如何打印可选字符串

Java 8 嵌套 null 检查列表中映射中的字符串

php - 如何告诉 PHP 在函数调用中使用默认参数?

c# - 使用空合并运算符进行比较

swift - UITableView Cell 显示问题

ios - 如何从 iOS 中的音频剪辑中检索 PCM 样本数据?

swift - 在 Swift 中使用不带逗号、换行符和括号的可变参数打印字符串

f# - F# 中的空合并运算符?

javascript - JavaScript中有 "null coalescing"运算符吗?