Goでは、ほとんどの組み込みデータ型(例えば、int、float、struct)は、変数として宣言されると**可変(mutable)**です。なぜなら、値渡しで渡すと、構造体全体や値がコピーされるからです。しかし、重要な点があります:スライス(slice)、マップ(map)、チャネル(channel)は、内部データストレージのロジックを持つ参照型です。
基本的な型:
x := 10 y := x // ここで値のコピーが作成され、xとyは互いに影響しない y = 20 // xは10のまま
スライス:
a := []int{1,2,3} b := a // 同じ基本配列のスライスへの参照 b[0] = 100 // これでa[0]も100になる
関数やメソッドが構造体やスライスを受け取る場合、重要です。変更が元のデータに影響を与えるかどうかは、データ型と渡し方(値渡しか参照渡しか)によります。
質問: 「スライスを別のスライスに割り当てると、深いコピーが得られますか、それとも両方が同じデータを指しますか?」
回答: スライスの単純な割り当て(b := a)では、両方が同じ基本配列を指し示しますが、長さと容量は独立しています。一方のスライスから配列のデータを変更すると、もう一方に反映されます。
例:
a := []int{1,2,3} b := a b[0] = 42 fmt.Println(a) // [42 2 3]
深いコピーを作成するには、copyを使用します:
c := make([]int, len(a)) copy(c, a) c[0] = 99 fmt.Println(a) // [42 2 3], cは独立したコピー
逸話
データフィルタリングサービスが予期しない結果を返した: 一人の開発者がスライスを複数の関数間でコピーせずに渡し、その状態を変更していました。このため、異なる場所でのスライスの変更がアプリケーションのロジックを壊し、奇妙なデータバグが長い間見つからなかったのです。
逸話
構造体渡し後のデータ損失: 一つのプロジェクトで、データをシリアル化する際に必須フィールドを構造体から誤って削除しました。これは、オブジェクトが参照で渡され、コピーが作成されなかったからです。この結果、運用環境で重要な情報が失われました。
逸話
マップの並列処理に関するエラー: 開発者はマップへの参照をコピーし、変更が元のものに影響しないと考えましたが、これは他の言語では違う場合があります。その結果、異なるゴルーチンでデータ競合が発生し、アプリケーションがクラッシュしました。