了解Go中的变量范围

时间:2018-09-25 17:15:31

标签: go scope variable-declaration type-declaration

我正在通过Go规范来学习该语言,这些要点摘自“ Declarations and scope下的规范。”

尽管我能理解1-4点,但我对5点和6点感到困惑:

  
      
  1. 内部声明的常量或变量标识符的范围   函数从ConstSpec或VarSpec(ShortVarDecl   用于简短的变量声明),并在   最里面的包含块。
  2.   
  3. 在函数内部声明的类型标识符的范围始于   TypeSpec中的标识符,在最里面的末尾结束   包含块。
  4.   

这是我用来理解Go中范围的代码:

package main

import "fmt"

func main() {
    x := 42
    fmt.Println(x)
    {
        fmt.Println(x)
        y := "The test message"
        fmt.Println(y)
    }
    // fmt.Println(y) // outside scope of y
}

通过这段代码,我了解到x的范围在main函数之内,而y的范围在fmt.Println(x)之后的左括号和右括号中,而且我不能在右括号之外使用y

如果我正确理解的话,第4点和第5点都在说同样的话。所以我的问题是:

  1. 如果他们说的是同一句话,那么两者的重要性是什么 点?

  2. 如果它们不同,请让我知道其中的不同吗?

2 个答案:

答案 0 :(得分:4)

他们用相同的规则针对两个不同的观点提出了相同的观点:第一个关于变量和常量,第二个关于类型标识符。因此,如果在块内声明类型,则作用域规则与在同一位置声明的变量相同。

答案 1 :(得分:2)

除了适用于不同的事物(规则#5适用于constant-variable declarations,规则#6适用于type declarations)之外,措辞上还有一个重要区别:

  
      
  1. 内部声明的常量或变量标识符的范围   函数开始于ConstSpec或VarSpec(ShortVarDecl   用于简短的变量声明),并在   最里面的包含块。
  2.   
  3. 在函数中声明的类型标识符的范围始于   TypeSpec中的标识符,并在最里面的末尾结束   包含块。
  4.   

这就是为什么有2条规则而不是1条的原因。

这是什么意思?区别意味着什么?

#5变量和常量声明(在函数内部)

已声明的变量或常量的范围始于声明的末尾。这意味着,如果您要创建一个函数变量,并使用匿名函数对其进行初始化,则它不能引用自身。

这是无效的:

f := func() {
    f()
}

尝试编译:

prog.go:5:3: undefined: f

这是因为声明在匿名函数的右括号后结束,因此在其中不能调用f()。解决方法是:

var f func()
f = func() {
    f()
}

现在,f的声明在右括号(其类型为func())的结尾处结束,因此,在下一行中,当我们为其分配匿名函数时,引用{ {1}}(以调用存储在f变量中的函数值),因为它现在在范围内。查看相关问题:Define a recursive function within a function in Go

类似地,在初始化变量时,例如使用composite literal时,您将无法引用其中的变量:

f

这会产生一个编译时错误(“未定义:m”),因为var m = map[int]string{ 1: "one", 21: "twenty-" + m[1], } 不在范围内,而是在复合文字中。

显然,此替代方法有效:

m

#6类型声明(在函数内部)

声明类型的范围始于声明中的标识符。因此,与规则5相比,在声明中引用类型本身是有效的。

它有什么优势/意义吗?

是的,您可以声明递归类型,例如:

var m = map[int]string{
    1: "one",
}
m[21] = "twenty-" + m[1]

类型标识符type Node struct { Left, Right *Node } 出现在类型声明之后,该标识符在类型声明中,并以右括号结束,但在此之前我们可以有意义地引用它。

另一个示例是其元素类型为自身的切片类型:

Node

您可以在此处了解更多信息:How can a slice contain itself?

还有另一个有效的例子:

type Foo []Foo