【问题标题】:panic: assignment to entry in nil map on single simple map恐慌:在单个简单地图上分配零地图中的条目
【发布时间】:2016-06-04 05:02:52
【问题描述】:

我的印象是,只有当我们想要分配给双映射时,才会发生分配给 nil 映射错误的条目,也就是说,当尝试分配更深级别的映射而更高级别的映射时不存在,例如:

var mm map[int]map[int]int
mm[1][2] = 3

但它也发生在一个简单的地图上(尽管以结构为键):

package main

import "fmt"

type COO struct {
    x int
    y int
}

var neighbours map[COO][]COO

func main() {
    for i := 0; i < 30; i++ {
        for j := 0; j < 20; j++ {
            var buds []COO
            if i < 29 {
                buds = append(buds, COO{x: i + 1, y: j})
            }
            if i > 0 {
                buds = append(buds, COO{x: i - 1, y: j})
            }
            if j < 19 {
                buds = append(buds, COO{x: i, y: j + 1})
            }
            if j > 0 {
                buds = append(buds, COO{x: i, y: j - 1})
            }
            neighbours[COO{x: i, y: j}] = buds // <--- yields error
        }
    }


    fmt.Println(neighbours)

}

可能出了什么问题?

【问题讨论】:

    标签: dictionary go


    【解决方案1】:

    你需要初始化邻居:var neighbours = make(map[COO][]COO)

    参见第二部分:https://blog.golang.org/go-maps-in-action

    每当您尝试将值插入尚未初始化的映射时,您都会感到恐慌。

    【讨论】:

    • 所以我们实际上必须写两次定义?当值改变时,它必须在 2 个地方改变。看起来很奇怪。至少它奏效了。
    • 所以目前你的代码声明neighbours 将存在,但它实际上并没有为它请求内存。我们必须先为其请求内存,然后才能将任何数据放入其中。所以与其说写两次定义,不如说是确保我们为地图获得内存。
    • 哦,可以,这就是我写var neighbours = make(map[COO][]COO)的原因,你可以用它代替var neighbours map[COO][]COO。混淆可能源于这样一个事实,即 nil 切片可用于正常切片操作,但 nil 映射不适用于映射操作。
    • 本可以发誓当我尝试它时它会给出错误消息,但是从你那里复制就可以了!非常感谢。
    【解决方案2】:

    在 Golang 中,一切都被初始化为 zero value,它是未初始化变量的默认值。

    因此,正如我们所设想的那样,地图的零值为零。尝试使用未初始化的地图时,它会出现恐慌。 (一种空指针异常)

    有时它可能很有用,因为如果您知道某物的零值,则不必显式初始化它:

    var str string
    str += "42"
    fmt.Println(str)
    // 42 ; A string zero value is ""
    
    var i int
    i++
    fmt.Println(i)
    // 1 ; An int zero value is 0
    
    var b bool
    b = !b
    fmt.Println(b)
    // true ; A bool zero value is false
    

    如果你有Java背景,那也是一样:原始类型有默认值,对象初始化为null;

    现在,对于更复杂的类型,如chanmap,零值是nil,这就是为什么你必须使用make 来实例化它们。指针也有零值。数组和切片的情况有点棘手:

    var a [2]int
    fmt.Println(a)
    // [0 0]
    
    var b []int
    fmt.Println(b)
    // [] ; initialized to an empty slice
    

    编译器知道数组的长度(不能更改)及其类型,因此它已经可以实例化正确数量的内存。所有值都被初始化为零值(与 C 不同,您可以在数组中包含任何内容)。对于切片,初始化为空切片[],所以可以正常使用append

    现在,对于结构,它与数组相同。 Go 创建一个结构体,其所有字段都初始化为零值。它进行了深度初始化,示例如下:

    type Point struct {
        x int
        y int
    }
    
    type Line struct {
        a Point
        b Point
    }
    
    func main() {
        var line Line
        // the %#v format prints Golang's deep representation of a value
        fmt.Printf("%#v\n", line)
    }
    // main.Line{a:main.Point{x:0, y:0}, b:main.Point{x:0, y:0}}
    

    最后,interfacefunc 类型也被初始化为 nil。

    这就是它的全部内容。使用复杂类型时,您只需要记住初始化它们。唯一的例外是数组,因为你不能这样做make([2]int)

    在你的情况下,你有切片的地图,所以你至少需要两个步骤来把东西放在里面:初始化嵌套的切片,并初始化第一个地图:

    var buds []COO
    neighbours := make(map[COO][]COO)
    neighbours[COO{}] = buds
    
    // alternative (shorter)
    neighbours := make(map[COO][]COO)
    
    // You have to use equal here because the type of neighbours[0] is known
    neighbours[COO{}] = make([]COO, 0)
    

    【讨论】:

    • 也许我误解了你写的内容,但你不必初始化一个切片来追加它(一个 nil 切片是有效的追加到它)。
    • 我没有意识到这一点,感谢您纠正我!我相应地更新了我的帖子。
    • np!如果我不知道我实际需要什么容量,我只是懒惰并且喜欢使用 nil 切片开始。哈哈。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-22
    • 1970-01-01
    • 2019-01-14
    • 2013-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多