2

I think this question where asked several times but I still confused:

I have following code:

type obj struct {
    s *string
}

var cmdsP = []*string {
    stringPointer("create"),
    stringPointer("delete"),
    stringPointer("update"),
}

var cmds = []string {
    "create",
    "delete",
    "update",
}

// []*string
func loop1() {
    slice := make([]obj, 0, 0)

    for _, cmd := range cmdsP {
        slice = append(slice, obj{cmd})
    }
    for _, o := range slice {
        fmt.Println(*o.s)
    }
}

// []string
func loop2() {
    slice := make([]obj, 0, 0)
    for _, cmd := range cmds {
        slice = append(slice, obj{&cmd})
    }
    for _, o := range slice {
        fmt.Println(*o.s)
    }
}

func stringPointer(v string) *string {
    return &v
}

https://play.golang.org/p/65Le_8Pi3Mi

The only difference is in slice semantic []*string and []string how does it change behavior of cmd variable? Could you please draw or explain in details what happens in memory during iteration through two loops?

Rudziankoŭ
  • 10,681
  • 20
  • 92
  • 192

2 Answers2

10

When you call range on a collection, the go runtime initialises 2 memory locations; one for the index (in this case _), and one for the value cmd.

What range then does, is take each of the items in the collection and copy them into the memory location that it created when you called range.

This means that each of the items in the slice get put into that memory location one by one.

When you do &cmd you are taking a pointer. This pointer points to the shared memory location that each of the slice items are being copied into.

All of your pointers created using &cmd all point to the same memory location.

This means that after the range is done, the only value left in that memory location that your pointers are pointing to is the last value from the range iteration.

This is why you get the output

update
update
update
Zak
  • 5,515
  • 21
  • 33
1

This is because in one case you are passing an address to struct and in other case you are passing a string value. So whenever you append an struct field to a slice it will going to update the existing value on same address. That's the reason you are getting only last value for pointer type slice of structs which is update in this case

// []string
func loop2() {
    slice := make([]obj, 0, 0)
    for _, cmd := range cmds {
        slice = append(slice, obj{&cmd})
    }
    for _, o := range slice {
        fmt.Println(*o.s)
    }
}

&cmd will point to same address which is updating its value to the same location in every iteration which is not the case when you are not passing address to cmd in loop1.

Edited This is a slice of []obj which has a field of pointer type *string

slice := make([]obj, 0, 0)

So when you are looping over it you are actually passing the address of cmd and assigning it to a pointer field.

To see the difference print the type and you will get the information for eg:

func loop1() {
    slice := make([]string, 0, 0)

    for _, cmd := range cmdsP {
        slice = append(slice, *cmd)
    }
    for _, o := range slice {
        fmt.Println(o)
    }

    fmt.Printf("%#v\n", slice)
}

Playground example

In above case you are creating a slice of string.

// []*string
func loop1() {
    slice := make([]obj, 0, 0)

    for _, cmd := range cmdsP {
        slice = append(slice, obj{cmd})
    }
    for _, o := range slice {
        fmt.Println(*o.s)
    }
    fmt.Printf("%#v\n", slice)
}

While In second case you are creating a slice of obj struct which contains a field of pointer to string. They both are different cases. use fmt package to see the actual types of slices you are creating in loop1 function

Playground example

Himanshu
  • 12,071
  • 7
  • 46
  • 61
  • Thank you for the answer! You said `So whenever you append an struct field to a slice` but if I append string field `slice := make([]string, 0, 0)` everything is fine. What is the difference? https://play.golang.org/p/7UdLlP2GiLw – Rudziankoŭ Jul 12 '18 at 14:14
  • @Rudziankoŭ Please check edited answer – Himanshu Jul 12 '18 at 14:21