Pointing map to a new map inside a method looses effect after leaving method - go

This code, visible on the Go PlayGround:
package main
import "fmt"
type MyType map[int]int
func (x MyType) updateIt() {
newItem := make(MyType)
for i := 0; i < 3; i++ {
newItem[i] = i
}
x = newItem
fmt.Println(x)
}
func main() {
x := make(MyType)
x.updateIt()
fmt.Println(x)
}
Produces:
map[0:0 1:1 2:2]
map[]
While I expect:
map[0:0 1:1 2:2]
map[0:0 1:1 2:2]
Why?
My understanding is that: x.updateIt() takes x as an argument, then creates newItem and changes the pointer of x so that it points to newItem. So x points to the data of newItem. This seems to be indeed the case inside updateIt, as visible from the first print line. But when x is print after the method, the change is lost.
I'm new to Go and this is unexpected behaviour for me, could you please explain?

You should be using a pointer receiver:
func (x *MyType) updateIt() {
newItem := make(MyType)
for i := 0; i < 3; i++ {
newItem[i] = i
}
*x = newItem
fmt.Println(x)
}
Playground: https://play.golang.org/p/K82TTjHdDgg
Explanation
Arguments "in front" of a function (so-called receivers) behave more or less like ordinary arguments.
Imagine a function like this:
func updateIt(x MyType) {
newItem := make(MyType)
for i := 0; i < 3; i++ {
newItem[i] = i
}
x = newItem
fmt.Println(x)
}
Here, you pass x by value, not by reference (or as a pointer).
What happens is that x gets replaced inside the function, but keeps its original value outside of it. By passing a pointer you can replace the value to which x is pointing, thereby changing the value of x outside of the function.
See also "A Tour of Go, Pointers": https://tour.golang.org/moretypes/1

Inside the main function you create a new map and assign the pointer of that new map to x.
Then the value of that pointer is passed to the updateIt function.
Then inside updateIt you replace it with pointer to another map you create inside updateIt. Then it updates this new map instead of updating the original map created inside main
The most important thing to understand here is that your program creates two different maps.
So avoid creating two maps and update the same map passed to your updateIt function.
func (x MyType) updateIt() {
for i := 0; i < 3; i++ {
x[i] = i
}
fmt.Println(x)
}
https://play.golang.org/p/-MemfTv1uJV

Related

Golang: accessing map object outside the function it was declared in

I would like to loop through a slice of structs, and populate a struct field (which is a map) by passing in each struct to a function.
I have the below struct
type thing struct {
topicThing map[string]int
}
and I have the below functions
func main() {
ths := make([]thing, 0)
for i := 0; i < 10; i++ {
var th thing
ths = append(ths, th)
}
for _, th := range ths {
dothing(&th)
}
for _, th := range ths {
fmt.Println(th.topicThing)
}
}
func dothing(th *thing) {
tc := make(map[string]int)
tc["Hello"] = 1
tc["Bye"] = 2
th.topicThing = tc
}
The main function creates a slice of things (refered as ths), and passes each thing to the dothing() function by iterating over them.
Within dothing(), I create a new map, populate it with data, and assigns it to the passed in thing's attribute. However, by the time we iterate over ths in the main function to print topicThing of each thing, the map is empty.
Since make() creates objects within the heap, I was hoping it would be accessible even outside of the function scope. Can anyone tell me why this is happening?
P.S.
if I change the dothing() function like below:
func dothing(th *thing) {
th.topicThing["Hello"] = 1
th.topicThing["Bye"] = 2
}
The code works as expected, meaning the map is populated with data when accessed in the main function.
The range copies your object.
So when you do this,
for _, th := range ths {
dothing(&th)
}
you are actually dothing on a copy.
For example, with this main:
func main() {
ths := make([]thing, 0)
for i := 0; i < 10; i++ {
var th thing
ths = append(ths, th)
}
for _, th := range ths {
dothing(&th)
fmt.Println(th.topicThing)
}
it will print the right thing, since we are still working on the copy.
In order to not copy, use the array index:
for idx, _ := range ths {
dothing(&ths[idx])
}

How to declare variable types for loop variables in Go?

See this code.
package main
import (
"fmt"
)
func main() {
var arr [4]string = [4]string{"foo", "bar", "baz", "qux"}
for a, b := range arr {
fmt.Println(a, b)
}
// How can I fix this code?
/*
for x int, y string = range arr {
fmt.Println(a, b)
}
*/
}
The first for loop uses the := operator to automatically deduce the types of a and b. But what if I want to explicitly specify the types of the loop variables? My attempt to do this is in the second block of commented code which of course failed with the following error.
# command-line-arguments
./foo.go:15: syntax error: unexpected name, expecting {
./foo.go:18: syntax error: unexpected }
Can you help me fix the second block of code such that I can specify the types of x and y explicitly?
Unfortunately the language specification doesn't allow you to declare the variable type in the for loop. The closest you could get is this:
var a int
var b string
for a, b = range arr {
fmt.Println(a, b)
}
But normally if you give your variable meaningful names, their type would be clear as well:
for index, element := range arr {
fmt.Println(index, element)
}
You need to declare first the vars.
var x int
var y string ...// now it should be ok.
for x,y = range arr {
fmt.Println(x, y) // it should be x and y instead of a,b
}
Check the fiddle
First of all your code is not a valid Go code. The for range loop returns the index and the value of an array, slice, string, or map, so there is no reason the explicitly specify the type of the value and the index.
You are specifying the type of the values at the variable initialization, and the language will deduce the type on the range iteration.
One special case is when you are using interface{} as variable type. In this case, you if you need to know the type of the value you can use the reflect package to deduce the type of the value.
switch reflect.TypeOf(t).Kind() {
case reflect.Slice:
s := reflect.ValueOf(t)
for i := 0; i < s.Len(); i++ {
fmt.Println(s.Index(i))
}
}
It's not possible as you are trying to declare two different types of data in same line, if you want explicitly declare variables, then you need to declare them before itself like above answers, but if you want them to be of other type then you need to covert them as for your needs,
package main
import (
"fmt"
)
func main() {
var arr = [4]string{"foo", "bar", "baz", "qux"}
var x int64
var b []byte
for x = 0; x < int64(len(arr)); x++ {
b = []byte(arr[x])
fmt.Println(x, b)
}
}

initializing a struct containing a slice of structs in golang

I have a struct that I want to initialize with a slice of structs in golang, but I'm trying to figure out if there is a more efficient version of appending every newly generated struct to the slice:
package main
import (
"fmt"
"math/rand"
)
type LuckyNumber struct {
number int
}
type Person struct {
lucky_numbers []LuckyNumber
}
func main() {
count_of_lucky_nums := 10
// START OF SECTION I WANT TO OPTIMIZE
var tmp []LuckyNumber
for i := 0; i < count_of_lucky_nums; i++ {
tmp = append(tmp, LuckyNumber{rand.Intn(100)})
}
a := Person{tmp}
// END OF SECTION I WANT TO OPTIMIZE
fmt.Println(a)
}
You can use make() to allocate the slice in "full-size", and then use a for range to iterate over it and fill the numbers:
tmp := make([]LuckyNumber, 10)
for i := range tmp {
tmp[i].number = rand.Intn(100)
}
a := Person{tmp}
fmt.Println(a)
Try it on the Go Playground.
Note that inside the for I did not create new "instances" of the LuckyNumber struct, because the slice already contains them; because the slice is not a slice of pointers. So inside the for loop all we need to do is just use the struct value designated by the index expression tmp[i].
You can use make() the way icza proposes, you can also use it this way:
tmp := make([]LuckyNumber, 0, countOfLuckyNums)
for i := 0; i < countOfLuckyNums; i++ {
tmp = append(tmp, LuckyNumber{rand.Intn(100)})
}
a := Person{tmp}
fmt.Println(a)
This way, you don't have to allocate memory for tmp several times: you just do it once, when calling make. But, contrary to the version where you would call make([]LuckyNumber, countOfLuckyNums), here, tmp only contains initialized values, not uninitialized, zeroed values. Depending on your code, it might make a difference or not.

Why golang slice is empty after initialization?

My question here is why the slice is empty in another func when the slice is global to the file?
Here's a piece of code:
package main
import "fmt"
type Vec3 struct {
x float32
y float32
z float32
}
var a []Vec3
func main() {
a := make([]Vec3, 0)
a = append(a, Vec3{2.0, 3.0, 4.0})
a = append(a, Vec3{3.4, 5.6, 5.4})
a = append(a, Vec3{6.7, 4.5, 7.8})
fmt.Printf("%+v\n", a)
doSomethingWithA();
}
func doSomethingWithA() {
fmt.Printf("%+v\n", a)
}
Output:
[{x:2 y:3 z:4} {x:3.4 y:5.6 z:5.4} {x:6.7 y:4.5 z:7.8}]
[]
This is a repl.it link too, if you want to take a look.
Thanks for your kind help.
You have redefined it here:
a := make([]Vec3, 0)
To use the same variable you should assign a value with = but not declare a new variable with :=
a = make([]Vec3, 0)
Short variable declarations
Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type.
You are re declaring a, so actually you not initializing the global var, try:
a = make([]Vec3, 0)

setting a value of a pointer does not work through interface{}

Below is slightly modified example from the go laws of reflection http://blog.golang.org/laws-of-reflection. The 2nd code section uses a pointer from a map[string]interface{} and it does not work, what am I doing wrong?
Thanks
//http://play.golang.org/p/LuMBUWLVT6
package main
import (
"fmt"
"reflect"
)
type T struct {
x float64
}
func (x T) RowMap() map[string]interface{} {
return map[string]interface{}{
"x": &x.x,
}
}
func main() {
// this section works as expected, x.x will be 7.1 when done
var x = T{3.4}
p := reflect.ValueOf(&x.x) // Note: take the address of x.
v := p.Elem()
v.SetFloat(7.1)
fmt.Println(x.x, x) // 7.1 {7.1}
// this section I do not understand why x.x is not being set to 7.1
x = T{3.4}
rowmap := x.RowMap()
p = reflect.ValueOf(rowmap["x"]) // rowmap["x"] => &x.x just like above, but is containted in interface{}
v = p.Elem()
v.SetFloat(7.1)
fmt.Println(x.x, x) // 3.4 {3.4} ?? huh, should be // 7.1 {7.1}
}
Elem returns the value that the interface v contains or that the pointer v points to.
Try printing the following and you'll see what you want to see but x does not change meaning it's never being updated.
fmt.Println(v.Float()) // 7.1
You need to pass a pointer to your method. Change your method signature to look like this
func (x *T) RowMap() map[string]interface{} {
Pass a pointer instead of a copy.
I've added some print statements that I think will help clear things up http://play.golang.org/p/xcFMicIPcP
Look at the address of x inside and outside of your method and see how they're different.

Resources