値渡しと参照渡し

関数への引数の渡し方には、値渡しと参照渡しがある。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 の中の xyfoo の中の xy が別のものだってことだ。

ところが、よくわからないんだけど、スライスやマップ(配列も?)の場合には、呼び出した関数の中で破壊的に変更すると、呼び出し元でも変更されている。これは参照渡しのように見える。

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]

これはスライスの変数が、スライス自体ではなくスライスへの参照を保持していて、それが仮引数へコピーされるから、らしい。ともかく、これは気を付けよう。

ちなみに、ポインタを使えば参照渡しと同等のことができる。ポインタについてはそのうち書く。