Swift 协议可选一致性通过非可选

Swift Protocol Optional conformance via Non-Optional

我有一个带有可选 属性 的协议。

大多数符合此协议的类型都会有一个匹配的可选 属性。但是,有一个具有相同类型和名称的非可选 属性。

protocol SomeProtocol {
    var foo: Int? { get }
}

struct StructA: SomeProtocol {
    let foo: Int?
}

struct StructB: SomeProtocol {
    let foo: Int // Type 'StructB' does not conform to protocol 'SomeProtocol'
}

按 Xcode 的“修复 - 您要添加协议存根吗?”按钮添加了 属性 的可选版本,但结构现在具有无效的重复变量名称:

struct StructB: SomeProtocol {
    let foo: Int
    var foo: Int? { return foo } // Invalid redeclaration of 'foo'
}

{ get }-only 的情况下,我曾假设这会“正常工作”,因为非可选总是满足可选的约束,类似于你可以如何 return具有可选 return 类型的函数中的非可选。但显然不是这样。

这对函数也是一样的;声明 func bar() -> Int.

的一致类型不满足协议的 func bar() -> Int?

有什么办法可以解决这个问题吗?我不想重命名变量或添加中间吸气剂。

Swift有考虑过这种情况吗?不允许非可选协议变量满足可选协议变量的合理性是什么?

如果协议提供了一个默认实现,returns一个可选的:

protocol SomeProtocol {
    var foo: Int? { get }
}

extension SomeProtocol {
    var foo: Int? { return nil }
}

protocol-conforming 类型然后可以提供 variable/function:

的覆盖 non-optional 版本
struct StructB: SomeProtocol {
    let foo: Int
}

我在 Swift Evolution 论坛上发现了这个讨论:

At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.

https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448

这个 Swift 团队还讨论了允许 non-optional 类型满足 optional-value 协议:

Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)

是的,完全正确!除了这改变现有代码行为的部分,所以我们必须非常小心。这被认为是 [SR-522] Protocol funcs cannot have covariant returns

的一部分

在此处的 Stack Overflow 上进行了跟踪:

有助于让编译器识别一致性,但您需要了解这可能产生的奇怪行为。

新符合类型的对象将 return 一个不同的值,具体取决于您将其转换为哪种类型(这也包括传递类型为协议的参数):

let bar = StructB(foo: 7)
let baz: SomeProtocol = bar
bar.foo                     // evaluates to 7
baz.foo                     // evaluates to nil (surprise!)

最近有人评论了相关的 Swift 错误票:"This can be quite surprising and perhaps could be considered a bug of its own?"

这绝对把我绊倒了。