I am following the go tour and something bothered me.
Maps must be created with make (not new) before use
Fair enough:
map = make(map[int]Cats)
However the very next slide shows something different:
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
This slide shows how you can ignore make
when creating maps
Why did the tour say maps have to be created with make
before they can be used? Am I missing something here?
Actually the only reason to use make to create a map is to preallocate a specific number of values, just like with slices (except you can't set a cap on a map)
m := map[int]Cats{}
s := []Cats{}
//is the same as
m := make(map[int]Cats)
s := make([]Cats, 0, 0)
However if you know you will have a minimum of X amount of items in a map you can do something like:
m := make(map[int]Cats, 100)// this will speed things up initially
Also check http://dave.cheney.net/2014/08/17/go-has-both-make-and-new-functions-what-gives
So they're actually right that you always need to use make
before using a map. The reason it looks like they aren't in the example you gave is that the make
call happens implicitly. So, for example, the following two are equivalent:
m := make(map[int]string)
m[0] = "zero"
m[1] = "one"
// Equivalent to:
m := map[int]string{
0: "zero",
1: "one",
}
Now, the reason to use make
vs new
is slightly more subtle. The reason is that new
only allocates space for a variable of the given type, whereas make
actually initializes it.
To give you a sense of this distinction, imagine we had a binary tree type like this:
type Tree struct {
root *node
}
type node struct {
val int
left, right *node
}
Now you can imagine that if we had a Tree
which was allocated and initialized and had some values in it, and we made a copy of that Tree
value, the two values would point to the same underlying data since they'd both have the same value for root
.
So what would happen if we just created a new Tree
without initializing it? Something like t := new(Tree)
or var t Tree
? Well, t.root
would be nil, so if we made a copy of t
, both variables would not point to the same underlying data, and so if we added some elements to the Tree
, we'd end up with two totally separate Trees
.
The same is true of maps and slices (and some others) in Go. When you make a copy of a slice variable or a map variable, both the old and the new variables refer to the same underlying data, just like an array in Java or C. Thus, if you just use new
, and then make a copy and initialize the underlying data later, you'll have two totally separate data structures, which is usually not what you want.