理解 Go 中的变量作用域
Understanding variable scope in Go
我正在通过 Go specification
学习语言,这些要点取自 Declarations and scope
下的规范。
虽然我能理解第 1-4 点,但我对 5
和 6
点感到困惑:
- The scope of a constant or variable identifier declared inside a
function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
for short variable declarations) and ends at the end of the
innermost containing block.
- The scope of a type identifier declared inside a function begins at
the identifier in the TypeSpec and ends at the end of the innermost
containing block.
这是我用来理解 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
}
据此我的理解是scope of x
在main
函数内,scope of y
在fmt.Println(x)
之后的左右括号内,我不能在右括号外使用 y
。
如果我没理解错的话,这两点4 and 5
说的是同一件事。所以我的问题是:
如果他们说的是同一件事,那么importance
积分?
如果它们不同,你能告诉我difference
吗?
他们在两个不同的事情上提出了相同的观点,相同的规则:第一个是关于变量和常量,第二个是关于类型标识符。因此,如果您在块内声明类型,则适用与在同一位置声明的变量相同的范围规则。
除了适用于不同的东西(规则#5适用于constant- and variable declarations, rule #6 is for type declarations),在措辞上也有一个重要的区别:
- The scope of a constant or variable identifier declared inside a
function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
for short variable declarations) and ends at the end of the
innermost containing block.
- The scope of a type identifier declared inside a function begins at
the identifier in the TypeSpec and ends at the end of the innermost
containing block.
这就是为什么有 2 个规则而不是一个的原因。
这是什么意思?差异意味着什么?
# 5 变量和常量声明(函数内)
声明的变量或常量的范围从声明的末尾开始。这意味着如果你正在创建一个函数变量,用匿名函数初始化它,它不能引用它自己。
这是无效的:
f := func() {
f()
}
正在尝试编译:
prog.go:5:3: undefined: f
这是因为声明在匿名函数的右括号之后结束,所以在其中不能调用f()
。解决方法是:
var f func()
f = func() {
f()
}
这里 f
的声明结束于右括号(其类型为 func()
),因此在下一行中,当我们为其分配匿名函数时,引用是有效的到 f
(调用存储在 f
变量中的函数值)因为它现在在范围内。参见相关问题:
类似地,在初始化变量时,例如使用 composite literal,您不能引用其中的变量:
var m = map[int]string{
1: "one",
21: "twenty-" + m[1],
}
这会产生编译时错误 ("undefined: m"),因为 m
还不在复合文字的范围内。
显然这个解决方法有效:
var m = map[int]string{
1: "one",
}
m[21] = "twenty-" + m[1]
# 6 类型声明(函数内)
声明类型的范围从声明中的标识符开始。因此,与规则 #5 相反,在其声明中引用类型本身是有效的。
有什么优势/意义吗?
是的,您可以声明递归类型,例如:
type Node struct {
Left, Right *Node
}
类型标识符 Node
在它出现在类型声明中之后就在范围内,类型声明以右括号结尾,但在此之前我们可以有意义地引用它。
另一个例子是一个切片类型,它的元素类型是它自己:
type Foo []Foo
您可以在此处阅读更多相关信息:How can a slice contain itself?
又一个有效的例子:
type M map[int]M
我正在通过 Go specification
学习语言,这些要点取自 Declarations and scope
下的规范。
虽然我能理解第 1-4 点,但我对 5
和 6
点感到困惑:
- The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.
- The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.
这是我用来理解 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
}
据此我的理解是scope of x
在main
函数内,scope of y
在fmt.Println(x)
之后的左右括号内,我不能在右括号外使用 y
。
如果我没理解错的话,这两点4 and 5
说的是同一件事。所以我的问题是:
如果他们说的是同一件事,那么
importance
积分?如果它们不同,你能告诉我
difference
吗?
他们在两个不同的事情上提出了相同的观点,相同的规则:第一个是关于变量和常量,第二个是关于类型标识符。因此,如果您在块内声明类型,则适用与在同一位置声明的变量相同的范围规则。
除了适用于不同的东西(规则#5适用于constant- and variable declarations, rule #6 is for type declarations),在措辞上也有一个重要的区别:
- The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.
- The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.
这就是为什么有 2 个规则而不是一个的原因。
这是什么意思?差异意味着什么?
# 5 变量和常量声明(函数内)
声明的变量或常量的范围从声明的末尾开始。这意味着如果你正在创建一个函数变量,用匿名函数初始化它,它不能引用它自己。
这是无效的:
f := func() {
f()
}
正在尝试编译:
prog.go:5:3: undefined: f
这是因为声明在匿名函数的右括号之后结束,所以在其中不能调用f()
。解决方法是:
var f func()
f = func() {
f()
}
这里 f
的声明结束于右括号(其类型为 func()
),因此在下一行中,当我们为其分配匿名函数时,引用是有效的到 f
(调用存储在 f
变量中的函数值)因为它现在在范围内。参见相关问题:
类似地,在初始化变量时,例如使用 composite literal,您不能引用其中的变量:
var m = map[int]string{
1: "one",
21: "twenty-" + m[1],
}
这会产生编译时错误 ("undefined: m"),因为 m
还不在复合文字的范围内。
显然这个解决方法有效:
var m = map[int]string{
1: "one",
}
m[21] = "twenty-" + m[1]
# 6 类型声明(函数内)
声明类型的范围从声明中的标识符开始。因此,与规则 #5 相反,在其声明中引用类型本身是有效的。
有什么优势/意义吗?
是的,您可以声明递归类型,例如:
type Node struct {
Left, Right *Node
}
类型标识符 Node
在它出现在类型声明中之后就在范围内,类型声明以右括号结尾,但在此之前我们可以有意义地引用它。
另一个例子是一个切片类型,它的元素类型是它自己:
type Foo []Foo
您可以在此处阅读更多相关信息:How can a slice contain itself?
又一个有效的例子:
type M map[int]M