🐐

「Go初心者がハマる!:=と=の違いと複数戻り値・スコープの罠を一発理解」

に公開

Go言語では、変数の宣言と代入に「=」と「:=」という2種類の演算子が存在します。特に複数戻り値を受け取る場合や、グローバル変数・ローカル変数の扱いで混乱しやすいため、ポイントを整理します。


基本(こんなのは当たり前だー‼︎)

当たり前すぎて恐縮ですが、我慢して聞いてください。

1. 「=」と「:=」の違い

:=(短縮宣言)
- 新しい変数の宣言と初期化を同時に行う。
- 関数内でのみ使用可能。
- 型は右辺の値から自動推論される。

go
num := 10 *// int型のnumを新規宣言し、10を代入*

=(代入)
- 既に宣言されている変数に値を再代入する。
- 新しい変数の宣言は行わない。
- グローバル変数や関数スコープ内の既存変数に値をセットする場合に使う。

go
var num int
num = 20 *// 既存のnumに20を代入*

2. 複数戻り値と「:=」「=」の使い分け

Goでは複数の戻り値を返す関数が多用されます。

このとき、戻り値を受け取る変数のうち宣言されている変数と、未宣言の変数が存在するときどうなるのさ?

例1:全て未宣言の場合

go
a, b := someFunc()

両方とも新規宣言。

例2:一部が既存の場合

go
var a int
a, b := someFunc()

aは再代入、bは新規宣言。

例3:全て既存の場合

go
var a, b int
a, b = someFunc()

両方とも再代入。


3. グローバル変数とローカル変数のシャドーイング

『なんや、:= ってめっちゃ便利やん。未宣言の変数に出会ったらガンガン使っちゃお!』と思ったそこのあなた、ちょい甘いです。

グローバルで宣言済みの変数を、関数内で:= しちゃうと思ってもみない挙動になります。

NG例(グローバル変数が更新されない)

go
var DB *sql.DB

func InitDB() {
    DB, err := sql.Open("postgres", ...)
    // ここでDBはローカル変数として新規宣言され、グローバル変数DBは更新されない
}

OK例(グローバル変数を更新する)

go
var DB *sql.DB

func InitDB() {
    var err error
    DB, err = sql.Open("postgres", ...)
    // ここでDBはグローバル変数、errはローカル変数
}

【まとめ】スコープ内での「:=」の挙動

  • 同じスコープ内で既に宣言されている変数には「:=」で新たにローカル変数が作られることはありません。
  • ただし、外側スコープ(グローバルなど)で宣言されている場合は、関数内で「:=」を使うとローカル変数として新たに宣言されてシャドーイングされる
変数の状態 := の挙動
未宣言 新規宣言+初期化
同じスコープで宣言済 再代入(新規宣言はされない)
外側スコープで宣言済 新たにローカル変数としてシャドーイングされる

最後に(ベストプラクティス)

  • グローバル変数を関数内で更新したい場合は、関数内で新たに同名の変数を宣言しないこと。
  • 複数戻り値で一部が未宣言の場合、:=でまとめて宣言・代入できるが、スコープに注意。
  • 関数内ですでに宣言されている変数には「:=」で新たにローカル変数は作られない。

Discussion