背景
Goの可変長引数を取る関数内で受け取った値の書き換えを行った際の挙動が直感的ではなく気になったので調べてみました
結論
- スライスを渡した場合には、元の配列が書き換わる
- 要素を一つずつ渡した場合には、元の配列は書き換わらない
実例
package main
import "fmt"
// 可変長引数を受け取る関数
func modifyVariadicArgs(nums ...int) {
nums[0] = 999
}
func main() {
s1 := []int{1, 2, 3}
s2 := []int{1, 2, 3}
// スライスを指定する
modifyVariadicArgs(s1...)
// スライスの要素を指定する
modifyVariadicArgs(s2[0], s2[1], s2[2])
fmt.Printf("s1: %v\n", s1) // s1: [999 2 3]
fmt.Printf("s2: %v\n", s2) // s2: [1 2 3]
}
(Go 1.23.1 で検証)
スライスを渡した場合は参照渡し、それぞれ要素を指定した場合は値渡しのような挙動になりました。
まとめ
可変長配列を引数に取る関数を実装するタイミングでは、呼び出し元の変数を書き換えられるか否かが確定しないということになります。
そもそも引数に受け取った値に対して変更を行うという実装が好みではないので自分はあまりやらないですが、既存のコードを読む際やどうしてもこのような実装をしなければいけないときに、可変長配列を引数にとる場合は気をつけたほうがよさそうです。