miller
发布于

Map values are not addressable

A Go map is implemented as a hash table, and hash tables need to move their elements around when the map grows or shrinks. For this reason Go doesn't allow taking the address of a map element:

package main

import "fmt"

type item struct {
    value string
}

func main() {
    m := map[int]item{1: {"one"}}

    fmt.Println(m[1].value) // reading a struct value is fine
    addr := &m[1]           // error: cannot take the address of m[1]
    // error: can't assign to struct field m[1].value in map
    m[1].value = "two"      
}

There's a proposal to allow assignment to a struct field (m[1].value = "two") since in this case the pointer to the value field isn't kept, only assigned through. There are no specific plans for when or if it will be implemented because of “subtle corner cases”.

As a workaround, the whole struct needs to be reassigned back to the map:

package main

type item struct {
    value string
}

func main() {
    m := map[int]item{1: {"one"}}
    tmp := m[1]
    tmp.value = "two"
    m[1] = tmp
}

Alternatively a map of pointers to a struct will also work. In this case a “value” of m[1] is of a type *item. Go doesn't need to take a pointer to the map value, since the value itself is already a pointer. The hash table will move the pointers around in memory, but if you take a copy of a value of m[1] it will keep pointing to the same item so all is well:

package main

import "fmt"

type item struct {
    value string
}

func main() {
    m := map[int]*item{1: {"one"}}
    // Go doesn't need to take address of m[1] here 
    // as it's a pointer already
    m[1].value = "two"      
    fmt.Println(m[1].value) // two

    addr := &m[1] // still same error: cannot take the address of m[1]
}

It's worth noting that slices and arrays don’t have this problem:

package main

import "fmt"

func main() {
    slice := []string{"one"}

    saddr := &slice[0]
    *saddr = "two"

    fmt.Println(slice) // [two]
}

浏览 (167)
点赞
收藏
评论