英文:
Where can we use Variable Scoping and Shadowing in Go?
问题
我找到了一些相关的帖子:
- https://stackoverflow.com/questions/33458547/go-variable-scope-and-shadowing
- https://stackoverflow.com/questions/24475153/golang-variable-scope-inside-if-statements
- https://stackoverflow.com/questions/36531188/limit-the-scope-of-variables-storing-error/36533655
此外,变量作用域和变量遮蔽有许多用例。
如果有任何代码示例或答案,将不胜感激。
英文:
Some related posts I've found:
- https://stackoverflow.com/questions/33458547/go-variable-scope-and-shadowing
- https://stackoverflow.com/questions/24475153/golang-variable-scope-inside-if-statements
- https://stackoverflow.com/questions/36531188/limit-the-scope-of-variables-storing-error/36533655
Also there are many use cases to Variable Scoping and Shadowing.
Any code samples or answers will be appreciated.
答案1
得分: 5
变量作用域和遮蔽:
-
Go语言使用块进行词法作用域:
- 预声明标识符的作用域是全局块。
- 声明在顶层(任何函数外部)的常量、类型、变量或函数(但不包括方法)的标识符的作用域是包块。
- 导入包的包名的作用域是包含导入声明的文件块。
- 方法接收器、函数参数或结果变量的标识符的作用域是函数体。
- 在函数内部声明的常量或变量标识符的作用域从ConstSpec或VarSpec(对于短变量声明为ShortVarDecl)的结束开始,直到最内层包含块的结束。
- 在函数内部声明的类型标识符的作用域从TypeSpec中的标识符开始,直到最内层包含块的结束。
- 在块中声明的标识符可以在内部块中重新声明。在内部声明的标识符在作用域内时,它表示内部声明所声明的实体。
-
包声明不是一个声明;包名不出现在任何作用域中。它的目的是标识属于同一包的文件,并指定导入声明的默认包名。
优点:
- 由于无法从外部作用域访问数据,数据完整性得到保护。
Go语言中不同形式的变量遮蔽:
- 限制变量作用域的Go语言方式(在语句内使用简写赋值):
package main
import "fmt"
func main() {
i := 10 //作用域:main
j := 4
for i := 'a'; i < 'b'; i++ {
// 在此块内部遮蔽了i
fmt.Println(i, j) //97 4
}
fmt.Println(i, j) //10 4
if i := "test"; len(i) == j {
// 在此块内部遮蔽了i
fmt.Println(i, j) // i= test , j= 4
} else {
// 在此块内部遮蔽了i
fmt.Println(i, j) //test 40
}
fmt.Println(i, j) //10 4
}
-
当"我们需要更多字母"时,这是一种很好的限制变量作用域的方式。
这也适用于需要更多局部变量或作用域的情况:使用
{
和}
配对:
优点:无需额外的if、for等语句。
package main
import "fmt"
func main() {
i := 1
j := 2
// 新的作用域:
{
i := "hi" //新的局部变量
j++
fmt.Println(i, j) //hi 3
}
fmt.Println(i, j) //1 3
}
- 另一种限制变量作用域的方式是使用函数调用:
优点:作用域限制,输入值类型参数可像局部变量一样使用。
缺点:调用/返回时间和堆栈使用:如果编译器没有进行优化。
package main
import "fmt"
func fun(i int, j *int) {
i++ //+nice: 作为局部变量使用,没有副作用
*j++ //+nice: 故意作为全局变量使用
fmt.Println(i, *j) //11 21
}
func main() {
i := 10 //作用域:main
j := 20
fun(i, &j)
fmt.Println(i, j) //10 21
}
- 另一种方式是遮蔽全局变量:
package main
import "fmt"
var i int = 1 //全局变量
func main() {
j := 2
fmt.Println(i, j) //1 2
i := 10 //遮蔽全局变量
fmt.Println(i, j) //10 2
fun(i, j) //10 2
}
func fun(i, j int) {
//i := 100 //错误::=左侧没有新的变量
//var i int = 100 //错误:在此块中重新声明了i
fmt.Println(i, j) //10 2
}
参考:Variable shadowing 和 Scope。
以及:Declarations and scope。
英文:
Variable scoping and shadowing:
> Go is lexically scoped using blocks:
>
> 1. The scope of a predeclared identifier is the universe block.
> 2. The scope of an identifier denoting a constant, type, variable, or
> function (but not method) declared at top level (outside any function)
> is the package block.
> 3. The scope of the package name of an imported
> package is the file block of the file containing the import
> declaration.
> 4. The scope of an identifier denoting a method
> receiver, function parameter, or result variable is the function body.
> 5. 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.
> 6. 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.
> An identifier declared in a block may be redeclared in an inner block.
> While the identifier of the inner declaration is in scope, it
> denotes the entity declared by the inner declaration.
>
> The package clause is not a declaration; the package name does not
> appear in any scope. Its purpose is to identify the files belonging to
> the same package and to specify the default package name for import
> declarations.
Advantages:
- Since data cannot be accessed from outer scope, Data Integrity is preserved
Different forms of Variable shadowing in Go:
-
Golang way to limit variable scope (using short-hand assignment inside statements):
<!-- language: lang-golang -->
package main import "fmt" func main() { i := 10 //scope: main j := 4 for i := 'a'; i < 'b'; i++ { // i shadowed inside this block fmt.Println(i, j) //97 4 } fmt.Println(i, j) //10 4 if i := "test"; len(i) == j { // i shadowed inside this block fmt.Println(i, j) // i= test , j= 4 } else { // i shadowed inside this block fmt.Println(i, j) //test 40 } fmt.Println(i, j) //10 4 }
-
When "we need more alphabets", this is nice way to limit variables scope.
Also this works well when you need more local variables or scope:using
{
and}
pair:
Pros: no need to extra statements like if, for, …<!-- language: lang-golang -->
package main import "fmt" func main() { i := 1 j := 2 //new scope : { i := "hi" //new local var j++ fmt.Println(i, j) //hi 3 } fmt.Println(i, j) //1 3 }
-
Another way to limit variable scope is using function calls:
Pros: scope limit, input value type parameters are usable like local variables,
Cons: call/return time, and stack usage: if it is not optimized by compiler<!-- language: lang-golang -->
package main import "fmt" func fun(i int, j *int) { i++ //+nice: use as local var without side effect *j++ //+nice: intentionally use as global var fmt.Println(i, *j) //11 21 } func main() { i := 10 //scope: main j := 20 fun(i, &j) fmt.Println(i, j) //10 21 }
-
Another way is shadowing global variables:
<!-- language: lang-golang -->
package main import "fmt" var i int = 1 //global func main() { j := 2 fmt.Println(i, j) //1 2 i := 10 //Shadowing global var fmt.Println(i, j) //10 2 fun(i, j) //10 2 } func fun(i, j int) { //i := 100 //error: no new variables on left side of := //var i int = 100 //error: i redeclared in this block fmt.Println(i, j) //10 2 }
See: Variable shadowing and Scope.
And: Declarations and scope:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论