【Go 语言入门很简单(Go 中的作用域和变量隐藏)】非淡泊无以明志,非宁静无以致远。这篇文章主要讲述Go 语言入门很简单:Go 中的作用域和变量隐藏相关的知识,希望能为你提供帮助。
变量隐藏在 Go 中可能会令人困惑,让我们尝试弄清楚。
package main
import (
"fmt"
"io/ioutil"
"log"
)
func main()
f, err := ioutil.TempFile("", "")
if err != nil
log.Fatal(err)
defer f.Close()
if _, err := f.Write([]byte("Hello World\\n")); err != nil
log.Fatal(err)
else
fmt.Println("All success")
请注意,我们首先从 ?
?TempFile?
? 函数创建了两个变量:??f?
? 和 ??err?
?。然后我们调用 ??Write?
? 丢弃写入的字节数。我们让函数在 ??if?
? 语句中调用它。让我们编译,它工作正常。$ go run main.go
All success
现在,与 ?
?if?
? 之外的 ??Write?
? 调用相同的代码:package main
import (
"fmt"
"io/ioutil"
"log"
)
func main()
f, err := ioutil.TempFile("", "")
if err != nil
log.Fatal(err)
defer f.Close()
// if _, err := f.Write([]byte("Hello World\\n")); err != nil
//log.Fatal(err)
//else
//fmt.Println("All success")
//
_, err := f.Write([]byte("Hello World\\n"))
if err != nil
log.Fatal(err)
else
fmt.Println("All success")
运行该代码:
$ go run main.go
# command-line-arguments
./main.go:23:9: no new variables on left side of :=
所以发生了什么事?
请注意,我们使用 ?
?:=?
? 调用 ??Write?
?,这意味着我们创建了一个新变量 ??err?
?。在第二个例子中,很明显,??err?
? 已经存在,所以我们不能重新声明它。但是为什么它第一次起作用呢?因为在 Go 中,变量是其作用域的本地变量。在第一个示例中,我们实际上在 ?
?if?
? 范围内隐藏了 ??err?
?。例如:
package main
func main()
var err error
_ = err
var err error
_ = err
这显然会失败,但是,如果我们限定第二个 ?
?err?
?,它会起作用!package main
func main()
var err error
_ = err
var err error
_ = err
包隐藏考虑以下代码:
package main
import "fmt"
func Debugf(fmt string, args ...interface)
fmt.Printf(fmt, args...)
起初,它看起来不错。我们从 ?
?fmt?
? 包中调用 ??Printf?
? 并将 ??fmt?
? 变量传递给它。函数声明中的 ?
?fmt?
? 字符串实际上隐藏了包,现在“只是”一个变量。编译器会抱怨:我们需要使用不同的变量名来保存对 ??fmt?
? 包的访问。全局变量需要考虑的是,一个函数已经是一个“子作用域”,它是全局作用域内的一个作用域。这意味着您在函数中声明的任何变量都可以在全局范围内隐藏某些内容。
正如我们之前看到的,变量可以映射包,全局变量和函数的概念是相同的。
类型强制
就像我们可以用变量或函数来映射一个包一样,我们也可以用任何类型的新变量来映射一个变量。阴影变量不需要来自同一类型。这个例子编译得很好:
package main
func main()
var a string
_ = a
var a int
_ = a
闭包
使用嵌入式函数时,作用域非常重要。函数中使用且未声明的任何变量都是对上层范围的引用。众所周知的使用 ?
?goroutine?
? 的例子:package main
import (
"fmt"
"time"
)
func main()
for _, elem := range []bytea, b, c
go func()
fmt.Printf("%c\\n", elem)
()
time.Sleep(1e9) // Sleeping to give time to the goroutines to be executed.
运行该代码:
$ go run main.go
c
c
c
这不是我们真正想要的。这是因为范围改变了 ?
?goroutine?
? 中引用的 ??elem?
?,因此在短列表中,它将始终显示最后一个元素。为了避免这种情况,有两种解决方案:
- 将变量传递给函数
package main
import (
"fmt"
"time"
)
func main()
for _, elem := range []bytea, b, c
go func(char byte)
fmt.Printf("%c\\n", char)
(elem)
time.Sleep(1e9)
运行结果:
$ go run main.go
a
c
b
- 在本地范围内创建变量的副本
package main
import (
"fmt"
"time"
)
func main()
for _, elem := range []bytea, b, c
char := elem
go func()
fmt.Printf("%c\\n", char)
()
time.Sleep(1e9)
运行该代码,可以得到我们想要的结果:
当我们将变量传递给函数时,我们实际上将变量的副本发送给以字符形式接收它的函数。因为每个 ?
?goroutine?
? 都有自己的副本,所以没有问题。当我们复制变量时,我们创建一个新变量并将 ?
?elem?
? 的值分配给它。我们在每次迭代中都这样做,这意味着对于每个步骤,我们都会创建一个新变量,??goroutine?
? 会引用该变量。每个 ??goroutine?
? 都有一个对不同变量的引用,并且它也可以正常工作。现在,我们知道我们可以隐藏变量,为什么还要更改名称呢?我们可以简单地使用相同的名称,因为它会影响上层范围:
package main
import (
"fmt"
"time"
)
func main()
for _, elem := range []bytea, b, c
go func(elem byte)
fmt.Printf("%c\\n",推荐阅读
- 5 张弹珠图彻底弄清 RxJS 的拉平策略(mergeMapswitchMapconcatMapexhaustMap)
- 扩展433兆赫射频发射模块的传输范围
- Flutter 专题101 何为 Flutter Elements (#yyds干货盘点#)
- #yyds干货盘点# Spring核心之控制反转(IOC)
- 路由基础之OSPF的监测和调试
- ThinkPHP上次和下载封装
- Cilium v1.10.6 安装部署
- 算法题每日一练---第36天(连续子数组的最大和)
- 云原生时代的搜索服务算力管理