理解为什么不允许进行Swift元组赋值

11
以下代码是正确的:
func intTest() -> Int? {
    var res : Int

    res = 5

    return res
}

将非可选的 Int 从返回类型为可选的 Int 的方法中返回是没有问题的。

现在让我们看看当返回值是一组可选值的元组时会发生什么:

func tupleTest() -> (Int?, Int?) {
    var res : (Int, Int)

    res = (5, 5)

    return res
}

return res这一行代码导致错误:

错误: 无法将元组转换 '(Int, Int)' 为 '(Int?, Int?)' (又名 '(Optional, Optional)')

为什么Swift不允许这样做?

我当然理解为什么在另一个方向上(从可选到非可选)会出现错误,但是为什么在元组中不能用非可选值代替可选值,而非元组可以正常工作呢?

我看过Swift元组到可选类型的赋值,但它并没有涉及为什么不能进行赋值,只是解决了如何解决这个问题。


2
这似乎是一个不错的功能。我不明白为什么元组成员不能晋升为“Optional”。 - Alexander
1个回答

7
发生这种情况的原因是类型不匹配。考虑以下情况:
let myClosure: (Int) -> ()
let myOtherClosure: (Int?) -> ()
myClosure的类型为(Int) -> (),而myOtherClosure的类型为(Int?) -> (),尽管它们的参数相似,但它们是基本不同的类型。当Swift查看这两个常量时,它会整体评估类型,而不是个别部分。您的元组也是如此;它将元组类型签名视为整个单元,而不是分解参数并识别它们为相同类型的非可选版本。
IntInt?的转换可以成功进行,因为它们是相同的类型,只是其中一个是可选的。从(Int, Int)(Int?, Int?)的转换失败了,因为元组的参数不同,导致整体类型不同,因此出现错误。
让我们看一下您的示例之一:
 func tupleTest() -> (Int?, Int?) {
    let first: Int = 1
    let second: Int = 2
    let res: (Int?, Int?) = (first, second)
    return res
 }

这样做是因为尽管值是非可选整数,但元组类型被标记为(Int?, Int?),而:

func tupleTest() -> (Int?, Int?) {
    let first: Int = 1
    let second: Int = 2
    let res = (first, second)
    return res
}

代码不能编译,因为现在res的类型是(Int, Int)。我不是Swift编译器的专家,但我猜测类型系统的工作方式是它不会分解函数或元组的每个单独部分,并认识到对应的返回参数是相同的类型,只是可选的。


感谢提供的详细信息。但是在我运行的一个测试中,使用您的myClosuremyOtherClosure变量得到了与预期相反的结果。如果将它们更改为var并交替分配一个变量给另一个变量,则可以奇怪地将(Int?) -> ()闭包分配给(Int) -> ()变量,但不能反过来。这与我的预期相反,进一步混淆了我的原始问题。 - rmaddy
@rmaddy,这很有道理,也是函数输入协变的一个例子。你可以将(Int?) -> ()分配给(Int) -> (),因为传入的任何Int输入都可以自由地提升为Int?,从而满足分配的函数签名。如果您考虑将(Int) -> ()分配给(Int?) -> (),那么您可以将可选输入传递给具有非可选参数的函数,因此该分配是非法的。 - Hamish
@Hamish 好的,经过更多的尝试,我现在明白了自己的困惑,并理解为什么你可以将 (Int?) -> () 分配给 (Int) -> (),而反过来则不行。 - rmaddy
好的,既然我在脑海中澄清了这个问题,但是这个答案仍然让我感到困惑。论点是你不能将(Int, Int)赋值给(Int?, Int?),因为它们是不同的类型。但是为什么可以将(Int?) -> ()分配给(Int) -> ()?它们也是不同的类型,但它确实有效。 - rmaddy
这听起来就像是开发者懒得按程序员的期望去实现它。这只是另一个例子,Swift被开发为“完美语言”,而不是易于(且仍然安全)构建应用程序的语言。 - Jonathan.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接