理解Go中的变量范围

Mah*_*h B 5 scope go variable-declaration type-declaration

我正在通过Go规范来学习语言,这些要点来自" 声明和范围 "中的规范.

虽然我能够理解第1-4点,但我对第5点和第6点感到困惑:

  1. 在函数内声明的常量或变量标识符的范围从ConstSpec或VarSpec(短变量声明的ShortVarDecl)结束开始,并在最内层包含块的末尾结束.
  2. 在函数内声明的类型标识符的范围从TypeSpec中的标识符开始,并在最内层包含块的末尾结束.

这是我用来理解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
}
Run Code Online (Sandbox Code Playgroud)

从这段代码中我了解到范围x是在main函数内,并且范围y是在开始和结束括号之后fmt.Println(x),并且我不能y在结束括号之外使用.

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

  1. 如果他们说同样的话,这两点的重要性是什么?

  2. 如果它们不同,您能告诉我区别吗?

icz*_*cza 7

除了适用于不同的事物(规则#5适用于常量变量声明,规则#6适用于类型声明),措辞上也有一个重要的区别:

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

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

这是什么意思?差异意味着什么?

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

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

这是无效的:

f := func() {
    f()
}
Run Code Online (Sandbox Code Playgroud)

尝试编译:

prog.go:5:3: undefined: f
Run Code Online (Sandbox Code Playgroud)

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

var f func()
f = func() {
    f()
}
Run Code Online (Sandbox Code Playgroud)

现在,这里的声明f以右括号结束(其类型为func()),因此在下一行中,当我们将匿名函数分配给它时,引用f(调用存储在f变量中的函数值)是有效的,因为它是现在在范围内。请参阅相关问题:Define a recursive function inside a function in Go

类似地,当使用复合文字初始化变量时,您不能引用其中的变量:

var m = map[int]string{
    1:  "one",
    21: "twenty-" + m[1],
}
Run Code Online (Sandbox Code Playgroud)

这会产生编译时错误(“undefined: m”),因为m不在复合文字的范围内。

显然这个解决方法是有效的:

var m = map[int]string{
    1: "one",
}
m[21] = "twenty-" + m[1]
Run Code Online (Sandbox Code Playgroud)

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

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

它有什么好处/意义吗?

是的,您可以声明递归类型,如下所示:

type Node struct {
    Left, Right *Node
}
Run Code Online (Sandbox Code Playgroud)

类型标识符Node出现在类型声明中之后就在范围内,类型声明以右括号结尾,但在此之前我们可以有意义地引用它。

另一个例子是切片类型,其元素类型是其自身:

type Foo []Foo
Run Code Online (Sandbox Code Playgroud)

您可以在这里阅读更多相关信息:切片如何包含自身?

还有一个有效的例子:

type M map[int]M
Run Code Online (Sandbox Code Playgroud)


Adr*_*ian 6

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