展开/打包多级可选

时间:2018-11-07 01:56:36

标签: swift optional 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??的有效值,因此不会返回默认值。

有什么办法可以安全地在这里做我想做的事吗?

1 个答案:

答案 0 :(得分:1)

此代码可以满足您的要求。 缺点是您需要在要放入可选内容中的每种类型中实现Unwrappable协议。也许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协议将可以最终解包的类型(例如,在0、1或多个解包之后)标记为特定类型。

此问题的难点在于,在Optional的扩展中可以获得Wrapped类型,但是如果Wrapped再次是可选的,则无法访问Wrapped.Wrapped(换句话说,Swift不会支持高级类型)。

另一种方法是尝试为Optional where Wrapped == Optional添加扩展名。但是同样,您需要更高类型的支持来访问Wrapped.Wrapped类型。如果我们尝试扩展Optional where Wrapped == Optional<T>,我们也会失败,因为我们无法在T上通用扩展Optional。