2

So, why are they (No.1 & No.2 below) different?


type T1 struct {
    local []string
}

func (t *T1) Assign(param ...string) {
    t.local = nil
    t.local = append(t.local, param...) // No.1 <<<
    t.local = param[:]                  // No.2 <<<
}

They ARE different, for sure: No.2 is quite "shallow".

When one change t.local[i], she will garble the original string if No.2 was in use.

jno
  • 997
  • 1
  • 10
  • 18
  • What do you mean they are different? In terms of end result they are the same... so you need to be more specific. What exactly are you asking about here? – super Jun 30 '20 at 08:16
  • The No.2 looks to be copying only references to the strings. When one change `t.local[i]` she will garble the original string if No.2 was in use. I ain't sure I formulate it good enough... – jno Jun 30 '20 at 08:18
  • AFAIK strings are always passed/copied by reference in go. The string object holds a pointer to the underlying data. – super Jun 30 '20 at 08:21
  • @super: Nothing is passed by reference in Go. See https://stackoverflow.com/q/47296325/13860 – Jonathan Hall Jun 30 '20 at 08:26
  • https://blog.golang.org/slices – Volker Jun 30 '20 at 08:56

1 Answers1

3

Your "No.1" approach appends to a nil slice which guarantees that a new backing array will be allocated if there are more than zero params provided.

Your "No.2" approach doesn't create a new slice, it just slices the param.

If Assign() is called by passing an existing slice, the 2nd approach will store that, and if its elements are modified, it will be reflected in the stored slice.

Let's modify your example a little to test it:

type T1 struct {
    local []string
}

func (t *T1) Assign1(param ...string) {
    t.local = nil
    t.local = append(t.local, param...) // No.1 <<<
}

func (t *T1) Assign2(param ...string) {
    t.local = nil
    t.local = param[:] // No.2 <<<
}

Testing it:

t1 := &T1{}

s := []string{"a", "b", "c"}
t1.Assign1(s...)
fmt.Println(t1.local)
s[0] = "x"
fmt.Println(t1.local)

s = []string{"a", "b", "c"}
t1.Assign2(s...)
fmt.Println(t1.local)
s[0] = "x"
fmt.Println(t1.local)

Output (try it on the Go Playground):

[a b c]
[a b c]
[a b c]
[x b c]

As you can see, when using Assing1(), the local slice is not affected by modifying the passed slice.

When using Assing2(), elements of the local slice reflect the changes made in the original.

Please read relevant blog posts:

The Go Blog: Go Slices: usage and internals

The Go Blog: Arrays, slices (and strings): The mechanics of 'append'

icza
  • 389,944
  • 63
  • 907
  • 827