A mistake that is easy to make with Go maps is unintentional use of pointers as map keys. For example, consider a map originally keyed on strings.
type UserID int
userIDs := make(map[string]UserID) // first name -> user ID
Requirements change and it must now use a combination of two strings as the key. This is commonly accomplished by introducing a struct that holds the two strings and using that as the key.
type Name struct {
FirstName string
LastName string
}
userIDs := make(map[*Name]UserID) // name -> user ID
1. Problem
The map uses pointers to struct values for its keys.
map[*Name]UserID
This will not yield the expected behavior.
userIDs[&Name{"Jack", "Sparrow"}] = UserID(42)
fmt.Println(userIDs[&Name{"Jack", "Sparrow"}])
// Output: 0
The expectation would be for the example to print 42
but it will print
0
.
1.1. Why
The map uses pointers for its keys.
The two &Name{..}
expressions are pointers to different memory addresses.
Suppose the first one has the address 0x000123
.
0x000123 +--------+-----------+ .---------------->| "Jack" | "Sparrow" | | +--------+-----------+ | userIDs[&Name{"Jack", "Sparrow"}] = UserID(42)
The map will use the numeric value, 0x000123
as the key.
userIDs +------+----------+-----+ | .... | 0x000123 | ... | +------+-----|----+-----+ | | +----+ '---->| 42 | +----+
The second &Name{..}
expression has a different memory address.
0x000456 +--------+-----------+ .---->| "Jack" | "Sparrow" | | +--------+-----------+ | | fmt.Println(userIDs[&Name{"Jack", "Sparrow"}])
The map does not recognize this numeric value so it will return the
zero-value of UserID
.
2. Solution
To match on the values of the fields of the Name
struct, use the
struct by value.
-make(map[*Name]UserID)
+make(map[Name]UserID)
This behaves as expected.
userIDs[Name{"Jack", "Sparrow"}] = UserID(42)
fmt.Println(userIDs[Name{"Jack", "Sparrow"}])
// Output: 42
This is true for use of sync.Map
as well.
var userIDs sync.Map
userIDs.Store(Name{"Jack", "Sparrow"}, UserID(42))
v, _ := userIDs.Load(Name{"Jack", "Sparrow"})
fmt.Println(v)
// Output: 42