admin管理员组文章数量:1392081
I am pretty new to Go. My expectation when initializing a variable that was passed by reference is that this variable will now be initialized everywhere it is in scope, but that does not seem to be the case. Can anyone explain why this is? Below is some example code:
package main
import "fmt"
func updateMap(m map[byte]bool) {
if m == nil {
m = make(map[byte]bool)
}
m['c'] = true
}
func main() {
myMaps := make([]map[byte]bool, 10)
for _, m := range myMaps {
updateMap(m)
fmt.Println(m)
}
}
The maps all print as empty, however if I move the initialization to the for loop, just before calling updateMap, then the maps print with one element each, as expected.
I am pretty new to Go. My expectation when initializing a variable that was passed by reference is that this variable will now be initialized everywhere it is in scope, but that does not seem to be the case. Can anyone explain why this is? Below is some example code:
package main
import "fmt"
func updateMap(m map[byte]bool) {
if m == nil {
m = make(map[byte]bool)
}
m['c'] = true
}
func main() {
myMaps := make([]map[byte]bool, 10)
for _, m := range myMaps {
updateMap(m)
fmt.Println(m)
}
}
The maps all print as empty, however if I move the initialization to the for loop, just before calling updateMap, then the maps print with one element each, as expected.
Share Improve this question asked Mar 11 at 16:54 glarikglarik 11 bronze badge 2- 3 The same advice from similar questions about a lot of other popular languages (JavaScript, Python, C, Java, many more) applies – there’s no such thing as “pass by reference” in Go. The values of some types (e.g. pointers, slices, and maps) are themselves references (these types are sometimes called “reference types”). – Ry- ♦ Commented Mar 11 at 17:00
- Maybe it's clearer for you if you look at it as a pointer: go.dev/play/p/5ndFUkjWUOs – Mr_Pink Commented Mar 11 at 17:12
2 Answers
Reset to default -1You are not passing a "reference", as m
holds a value.
This is possible of you pass a map
pointer to updateMap
.
Caveat
This will not work, because m
is a copy:
for _, m := range myMaps
So you need to access the slice by its index directly:
for i := range myMaps
Fixed code
Here is the code for reference:
package main
import "fmt"
func updateMap(m *map[byte]bool) {
if *m == nil {
*m = make(map[byte]bool) // Modify
}
(*m)['c'] = true
}
func main() {
myMaps := make([]map[byte]bool, 10)
for i := range myMaps {
updateMap(&myMaps[i]) // Reference
fmt.Println(myMaps[i])
}
}
Output
map[99:true]
map[99:true]
map[99:true]
map[99:true]
map[99:true]
map[99:true]
map[99:true]
map[99:true]
map[99:true]
map[99:true]
Remarks
I will note that it it generally better to pass in a value and return a value like Kamran Khalid suggests.
Pure functions can make the code easier to follow. When you mutate values inside a function, there are side-effects that can be undesirable and cause very difficult bugs to happen.
Your main
initialises a slice of maps (references), but those map references are themselves nil
since they are not initialised.
When updateMap
is then called, it receives a nil
reference; this should help understand that what has been passed is not a reference to a particular element in the slice, but (a copy of) the reference held in each element. Each reference being is nil
.
When updateMap
receives the nil
reference a new map is initialised and a reference to that new map then overwrites the nil
reference that was passed in. So when you set the c
key, you are setting it in a map which is only referenced by the m
variable in updateMap
.
This should then make it clear why initialising the maps in each slice element in the loop works differently.
for _, m := range myMaps {
m = make(map[byte]bool)
updateMap(m)
fmt.Println(m)
}
You did not post what your version of "moving initialisation to the loop" looked like, and it should be noted that the above does not modify the myMaps
slice of maps. It takes each element from myMaps
(still nil
in this version at the start of each iteration) and again overwrites that copy of the reference with a reference to a newly initialised map.
The Println
is being given that same, new reference and outputs the map as updated by updateMap
. But the corresponding element in the slice is still nil
, as will be seen if you range over myMaps
to inspect it again:
for _, m := range myMaps {
m = make(map[byte]bool)
updateMap(m)
fmt.Println(m)
}
for _, m := range myMaps {
fmt.Println(m)
}
The way to fix that is to range over the indexes of the slice and work with those directly, which might look similar to:
for i := range myMaps {
myMaps[i] = make(map[byte]bool)
m := myMaps[i]
updateMap(m)
fmt.Println(m)
}
You could modify updateMaps
to accept a *map[byte]bool
(pointer to map) but this would be considered unusual or, in Go parlance, non-idiomatic and therefore not something you should do without a very good reason, which I don't this is.
本文标签:
版权声明:本文标题:initialization - Calling make() inside a Go function does not initialize variable passed by reference - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744781247a2624726.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论