Sliceのレシーバによって参照される値はコピーにはならない

9/10追記 コピーはされるが、sliceはarrayへのポインタを持っていて、コピーしてもそのポインタは変わらないので参照先にarrayは同じ、と言う話だった


レシーバは明示的にポインタを渡さない限り値のコピーが渡されると思っていたがそういうことでもないようだ。

package main

import (
    "fmt"
)

type TestSlice []string

func (t TestSlice) printPointer1(){
    fmt.Printf("pointer in main:%p\n", t) // 同じポインタ
}

func (t *TestSlice) printPointer2(){
    fmt.Printf("pointer in main:%p\n", *t) // 同じポインタ
    fmt.Printf("pointer in main:%p\n", t) // 違うポインタ
    ts := *t
    ts[0] = "YES"
    // *t[0] = "YES" // invalid operation: t[0] (type *TestSlice does not support indexing)
}

func main() {
    t := TestSlice([]string{"one", "two", "three"})
    fmt.Printf("pointer in main:%p\n", t) // 同じポインタ
    t.printPointer1()
    t.printPointer2()
}

レシーバにSliceそのものを指定した場合、メソッドの中で参照されるレシーバのポインタは呼び出し元と同様となった。(=値のコピーではない) また、Sliceのポインタで値を受ける場合、その値はポインタのポインタのような形になるようだった。 この際*ts[0]を参照できないのは型の情報がないからという理解でいいのだろうか。いまいちわからない。

Frequently Asked Questions (FAQ) - The Go Programming Language とりあえずSliceやMapのレシーバはポインタ渡しのような動きをするらしい。 レシーバだからどうこうではなく引数でも同じ動きになる。

package main

import (
    "fmt"
)

type TestSlice []string

func test(t TestSlice) {
    fmt.Printf("pointer in main:%p\n", t) // 同じポインタ
}

func main() {
    t := TestSlice([]string{"one", "two", "three"})
    fmt.Printf("pointer in main:%p\n", t) // 同じポインタ    
    test(t)
}

MapやSliceはポインタを持った構造体のようなもので、言語仕様としてその「内部で持っているポインタ」をSliceのポインタとして使う、みたいな感じになっているんだろうか。 いまいち理解できていない気がする。

参考

pospome.hatenablog.com