为什么 guard let foo = foo 无效?

13

在Swift中,您可以使用if let可选绑定将可选项解包为具有相同名称的常量或变量:

func test()
{
  let a: Int? = 1

  if let a = a {
    print("a = \(a)")
  } 
}

对于if let语句中的所有内容,可选值a都会被拆包成普通的整数。

同样地,我可以使用守卫语句来达到类似的效果。

func test()
{
  let a: Int? = 1

  guard let requiredA = a else{
    return
  }
  print("a = \(requiredA)")
}

然而,我不能使用这样的代码:guard let a = a else

func test()
{
  let a: Int? = 1

  guard let a = a else{
    return
  }
  print("a = \(a)")
}

为什么不呢?

在 guard 语句中,如果 guard 语句的条件失败,则执行 else 子句并退出当前作用域。如果条件成功,则从 guard 语句的右括号到当前作用域的结尾创建一个新的变量/常量。

为什么我不能使用同名的变量/常量将可选项映射到当前作用域的剩余部分?

附言:我意识到这个问题不是这个网站的完美匹配。如果您有更好的建议,请告诉我应该去哪里问这个问题。


1
在我看来,if 的方式会在不同的作用域中创建两个变量,这是可以接受的。而 guard 的方式则会在同一作用域中创建两个同名变量,这将是一个奇怪的特殊情况,并且为了允许它而使变量解析规则变得复杂。虽然我猜测如此,但我不懂 Swift。 - user2357112
1个回答

16

你不能这样做的原因:

func test()
{
  let a: Int? = 1

  guard let a = a else{
    return
  }
  print("a = \(a)")
}

这是因为guard在同一个作用域内创建了新的变量,因此您在同一个作用域内有两个名为a的变量。一个是Int类型,另一个是Int?类型。这是不被允许的。

如果您这样做,您将得到与Definition conflicts with previous value完全相同的错误信息:

func test()
{
    let a: Int? = 1

    let a = a!
}

将其与以下内容进行比较:

func test()
{
    let a: Int? = 1

    if let a = a {
        print("a = \(a)")
    }
}
在这种情况下,新变量a是一个Int,仅存在于if的then从句的新范围内,因此这可以工作。
从评论中可以看到:

但我向您提交,封闭花括号后面到封闭作用域结尾的代码部分实际上是内部作用域。

我能理解您希望它是这样,但事实并非如此。如果真的是这种情况,那么您可以这样做,但它也会出错:
func test()
{
    let a: Int? = 1

    guard let b = a else{
        return
    }
    print("b = \(b)")

    let a = 5  // Definition conflicts with previous value

    print("a = \(a)")
}

guard 的美妙之处在于它不会创建新的作用域,当您反复使用 if let 解包可选项时(并在此过程中创建新的作用域),可以避免创建“死亡金字塔”。


有关此主题的更多见解,请参阅后续问题“ guard let foo = foo何时变得合法?”


但是我向您提交,封闭括号后面到封闭范围结束的代码部分实际上是一个内部范围。如果您创建一个新的变量/常量,它将存在于该点直到当前范围的结束。这与if let有何不同,在语句体内,局部定义的a会覆盖外部定义? - Duncan C
它与 if let 不同,因为在 if let 中,跟随 if let{ } 会创建一个新的作用域。guard 的美妙之处在于你不需要 { },从而避免了新作用域的死亡金字塔。 - vacawama
1
是的,我想这很有道理。这使得您使用的模式与if let不同,并导致一些尴尬的命名,比如我的例子中的reqiredA - Duncan C
1
当然可以,但这会导致越来越深层的嵌套,并且不会像使用guard一样给你带来“黄金路径”的优势。 - Duncan C
在这里也有同样的问题 https://stackoverflow.com/questions/58226900/swift-cant-unwrap-an-optional-inside-a-loop 但是我不明白为什么即使有这个好的解释它仍然不能工作。 - Louis Lac
显示剩余4条评论

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