関数への引数の渡し方には、値渡しと参照渡しがある。Go は基本的に値渡しだ。つまり、実引数の値が関数に渡されるときに仮引数にコピーされる。例を挙げよう。
package main import "fmt" func foo(x int, y string) (int, string) { x = 10 y += "def" fmt.Println(x, y) return x, y } func main() { x := 1 y := "abc" fmt.Println(x, y) foo(x, y) fmt.Println(x, y) }
^o^ > go run funccall.go 1 abc 10 abcdef 1 abc
関数 foo
の中で x
は再代入されているし、y
は破壊的に変更されているけど、それでも関数から出た後の出力を見ると、値が元に戻っている。main
の中の x
、y
と foo
の中の x
、y
が別のものだってことだ。
ところが、よくわからないんだけど、スライスやマップ(配列も?)の場合には、呼び出した関数の中で破壊的に変更すると、呼び出し元でも変更されている。これは参照渡しのように見える。
package main import "fmt" func foo(x []int) []int { x[0] *= 10 fmt.Println(x) return x } func main() { x := []int{ 1,2,3 } fmt.Println(x) foo(x) fmt.Println(x) }
^o^ > go run funccall2.go [1 2 3] [10 2 3] [10 2 3]
これはスライスの変数が、スライス自体ではなくスライスへの参照を保持していて、それが仮引数へコピーされるから、らしい。ともかく、これは気を付けよう。
ちなみに、ポインタを使えば参照渡しと同等のことができる。ポインタについてはそのうち書く。