Go初始化运算符,包范围的变量 – 困惑:

huangapple go评论83阅读模式
英文:

Go Initialization operator, package scoped variables - confused:

问题

以下是翻译好的内容:

以下代码可以正常工作,输出为:You chose Test 1

package main

import (
	"fmt"
)

type TNameMap map[int]string

var nameMap TNameMap

func init() {

	nameMap = make(TNameMap)
	nameMap[1] = "You chose Test 1"
	nameMap[2] = "You chose Test 2"
	nameMap[3] = "You chose Test 3"

}

func main() {

	fmt.Println(nameMap[1])

}

如果我在init()函数中注释掉第一行,即//nameMap = make(TNameMap),那么当main()函数运行时,会出现恐慌(panic),因为nameMap没有被初始化:

panic: runtime error: assignment to entry in nil map

但是,如果我在init()函数中使用nameMap := make(TNameMap)而不是nameMap = make(TNameMap),我就不会出现恐慌(panic),但也没有输出——main()函数只是运行并终止进程。

我理解,如果我使用初始化操作符:=,即nameMap := make(TNameMap),我声明了一个新的变量nameMap,它的作用域仅限于init()函数,因此对于main()函数来说,只有包级别的变量var nameMap TNameMap在作用域内,导致没有输出,因为包级别的var没有保存任何映射数据。

但是,我感到困惑:为什么在这种情况下我没有得到恐慌(panic)呢?如果main()函数调用了包级别的变量,而该变量从未被初始化,那么为什么没有恐慌(panic)呢?

英文:

The following code works correctly - output: You chose Test 1

package main

import (
	"fmt"
)

type TNameMap map[int]string

var nameMap TNameMap

func init() {

	nameMap = make(TNameMap)
	nameMap[1] = "You chose Test 1"
	nameMap[2] = "You chose Test 2"
	nameMap[3] = "You chose Test 3"

}

func main() {

	fmt.Println(nameMap[1])

}

If I comment out the first line in init() i.e //nameMap = make(TNameMap) , I get a panic when main() runs, because nameMap was never initialized:

panic: runtime error: assignment to entry in nil map

But - if in init() I write nameMap := make(TNameMap)

instead of nameMap = make(TNameMap) , I get no panic, but also no output - main() simply runs and process terminates.

I understand that if I use the Initialization operator - nameMap := make(TNameMap) - I have declared a new variable nameMap that is scoped only to the init() function and so only the package level variable var nameMap TNameMap is in scope for main(), resulting in no output, because the package level var holds no map data.

But, I am confused: Why don't I get the panic in that situation? If main() is making the call on the package var, it was never initialized - so why no panic?

答案1

得分: 4

根据Go规范:

一个空的映射(nil map)与一个空映射等价,只是不能添加元素。

这意味着你可以读取一个空的映射,但不能写入。就像panic所说的“在空映射中赋值”。如果你注释掉nameMap = make(TNameMap)这一行,程序会崩溃,因为你在init函数中尝试对其进行写入(这就是panic发生的地方)。如果你注释掉整个init函数,Println不会崩溃,因为你可以访问(读取)一个空的映射。

将赋值改为声明只是掩盖了真正的问题,实际上它使所有的赋值都变得有效,并且丢弃了结果。只要你使赋值有效(通过删除它们或创建一个临时变量),那么在Println中你将观察到相同的行为。

一个空的映射返回的值始终是映射值类型的零值。所以map[T]string返回""map[T]int返回0,依此类推。(当然,如果你使用val,ok := nilMap[key]进行检查,那么ok将为false)。

英文:

According to the Go spec:

> A nil map is equivalent to an empty map except that no elements may be
> added.

This means that you can read from a nil map, but not write. Just like the panic says "assignment to entry in nil map". If you comment out just the line nameMap = make(TNameMap) it will crash because you attempt to write to it in init (which is where the panic happens). If you comment out the entirety of init the Println will not crash because you're permitted to access (read from) a nil map.

Changing the assignment to a declaration is just masking the real issue here, what's happening is it's making all the assignments valid, and then discarding the result. As long as you make the assignments valid (either by removing them or making a temporary variable), then you will observe the same behavior in Println.

The value returned by a nil map is always the zero value of the value type of the map. So a map[T]string returns "", a map[T]int returns 0, and so on. (Of course, if you check with val,ok := nilMap[key] then ok will be false).

huangapple
  • 本文由 发表于 2014年3月10日 12:02:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/22291888.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定