`guard let foo = foo` 何时成为合法语法?

11

回到2016年11月,我发布了一个问题,询问为什么不能使用guard创建与可选类型同名的非可选变量的解包版本,就像if let一样:

链接:为什么guard let foo = foo无效?

当我写那个问题时,下面的代码将无法编译,显示“与前一个值定义冲突”的错误:

//Test of using guard to create an unwrapped version of a var, like if let
func guardTest(_ viewController: UIViewController?) -> UIViewController? {
  // Check if the current viewController exists
  print(String(describing: viewController))
  guard let viewController = viewController else {
    return nil
  }
  print(String(describing: viewController))

  return viewController
}

然而,我刚在工作中找到了一些可以做到这一点的代码,现在它可以编译而没有任何问题,并且可以实现我想要的功能!运行时,打印语句显示在防护条件之前foo是一个可选项,在之后则是一个已解包的可选项:

viewController = Optional(<TrochoidDemo.ViewController: 0x7ff16a039a00>)
viewController = <TrochoidDemo.ViewController: 0x7ff16a039a00>

如果你想试用的话,我在最新的开源项目中添加了一个名为guardTest(_:)的测试函数。它可以在Github上找到,链接为:https://github.com/DuncanMC/TrochoidDemo

我很高兴这个结构现在正如我所希望的那样工作,但是不清楚为什么它现在是合法的以及何时发生了改变。

有人知道是否对语言定义进行了最近的更改,使得这种结构能够运行而之前无法运行吗?


3
在函数中仍然无法重新定义另一个局部变量。这似乎与Swift允许你创建与函数输入参数同名的局部变量有关。我不知道这是否总是有效的,但现在var a = a是将输入参数转换为var的方式,因为您不能再在函数签名中加入var了。 - vacawama
2
好的,为什么被踩了?如果你认为我的问题很差,请解释一下原因。 - Duncan C
2
我当然点了赞。任何挑战我认为自己已经理解透彻的问题都是好问题。我有点希望 Stack Overflow 强制你在踩别人的评论时发表(建设性的,希望如此)评论,或者至少点赞别人的建设性评论。 - vacawama
1个回答

13

简而言之:

如果foo在另一个作用域中定义,那么guard let foo = foo是合法的。


这是你链接问题中的示例:

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

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

这仍然无法工作,因为guard语句试图在同一作用域中创建另一个变量a

这个例子:

//Test of using guard to create an unwrapped version of a var, like if let
func guardTest(_ viewController: UIViewController?) -> UIViewController? {
  // Check if the current viewController exists
  print(String(describing: viewController))
  guard let viewController = viewController else {
    return nil
  }
  print(String(describing: viewController))

  return viewController
}

它之所以有效果,与此原理相同:

func test(a: Int)
{
    print(type(of: a))  // Int

    let a = 3.14

    print(type(of: a))  // Double
}

该函数的参数在不同的作用域中定义,因此Swift允许您创建具有相同名称的局部变量。


2
好的,这很有道理。感谢您澄清。您的示例是合法的似乎有些奇怪,因为您的 let a = 3.14 使参数无法访问。 - Duncan C
这种情况经常发生。局部变量会隐藏来自封闭作用域的其他变量。让我困惑的是,为什么第一个 print(type(of: a)) 不会出错,但在这种情况下 let a = 3 就会出错:func test() { print(type(of: a)) let a = 3.14 print(type(of: a)) } - vacawama
我认为函数的参数在局部作用域中,而不是封闭作用域中。这个讨论明确指出参数实际上被视为来自封闭作用域。因此,当a是一个参数时,var a=a是合法的,因为它从封闭作用域中获取常量,并将其重新定义为当前作用域中的变量。 - Duncan C
函数参数和在for循环中创建的变量的作用域是相同的。考虑以下例子:for i in 1...3 { print(i); let i = 3.14; print(i) } - vacawama
在我看来,Swift 中有一个规则,就像 C 语言一样,花括号定义了一个新的作用域级别,包括函数花括号和 for..in 语句周围的语句花括号。(大部分都是显而易见的。对于函数来说,参数声明在花括号外部,因此参数被认为是函数体的外层作用域。) - Duncan C

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