我正在通过Go规范来学习该语言,这些要点摘自“ Declarations and scope下的规范。”
尽管我能理解1-4点,但我对5点和6点感到困惑:
- 内部声明的常量或变量标识符的范围 函数从ConstSpec或VarSpec(ShortVarDecl 用于简短的变量声明),并在 最里面的包含块。
- 在函数内部声明的类型标识符的范围始于 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
}
通过这段代码,我了解到x
的范围在main
函数之内,而y
的范围在fmt.Println(x)
之后的左括号和右括号中,而且我不能在右括号之外使用y
。
如果我正确理解的话,第4点和第5点都在说同样的话。所以我的问题是:
如果他们说的是同一句话,那么两者的重要性是什么 点?
如果它们不同,请让我知道其中的不同吗?
答案 0 :(得分:4)
他们用相同的规则针对两个不同的观点提出了相同的观点:第一个关于变量和常量,第二个关于类型标识符。因此,如果在块内声明类型,则作用域规则与在同一位置声明的变量相同。
答案 1 :(得分:2)
除了适用于不同的事物(规则#5适用于constant-和variable declarations,规则#6适用于type declarations)之外,措辞上还有一个重要区别:
- 内部声明的常量或变量标识符的范围 函数开始于ConstSpec或VarSpec(ShortVarDecl 用于简短的变量声明),并在 最里面的包含块。
- 在函数中声明的类型标识符的范围始于 TypeSpec中的标识符,并在最里面的末尾结束 包含块。
这就是为什么有2条规则而不是1条的原因。
这是什么意思?区别意味着什么?
已声明的变量或常量的范围始于声明的末尾。这意味着,如果您要创建一个函数变量,并使用匿名函数对其进行初始化,则它不能引用自身。
这是无效的:
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
声明类型的范围始于声明中的标识符。因此,与规则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