Golang 入門

Go 変数宣言

Go 言語において、変数はデータを保存し操作するための基本的なビルディングブロック(Building Block)です。変数を正しく宣言する方法をマスターすることは、効率的でメンテナンス性の高いコードを書くための重要な前提条件となります。

Go 言語には、主に2つの変数宣言方法があります。伝統的な var キーワードを使用する方法と、よりモダンな短縮変数宣言(Short Variable Declaration) := を使用する方法です。これら2つの方式にはそれぞれの長所と適したユースケースがあります。本章ではそれらの詳細を深く掘り下げ、Go プログラム内で自在にデータを管理できるようガイドします。

1. var キーワードによる宣言

var キーワードは、Go 言語において最も正規かつ明示的な変数宣言の方法です。構造化された構文を提供し、変数名と具体的な型を明確に指定することができます。

1.1 基本構文

var を使用して変数を宣言する標準的な構文は以下の通りです。

var 変数名 変数タイプ

具体的な例を見てみましょう。

var age int         // 'age' という名前の整数 (int) 型変数を宣言
var name string     // 'name' という名前の文字列 (string) 型変数を宣言
var isLoggedIn bool // 'isLoggedIn' という名前のブール (bool) 型変数を宣言

極めて重要なポイント: 上記の宣言では、値を代入していない場合でも、Go 言語は自動的に「ゼロ値 (Zero Value)」を割り当てます。

  • int(整数): ゼロ値は 0
  • string(文字列): ゼロ値は ""(空の文字列)。
  • bool(ブール値): ゼロ値は false

ゼロ値に関する詳細は、後の章で詳しく説明します。

1.2 宣言と初期化

変数の宣言と同時に、初期値を直接与えることができます。

var age int = 30                 // 整数型変数 'age' を宣言し、30を代入
var name string = "田中太郎"      // 文字列型変数 'name' を宣言し、"田中太郎"を代入
var isLoggedIn bool = true       // ブール型変数 'isLoggedIn' を宣言し、trueを代入

1.3 型推論 (Type Inference)

Go 言語は非常にスマートです。宣言時に初期値を提供する場合、Go は通常、変数の型を自動的に推論(インファレンス)できます。この時、型の宣言を省略してコードをより簡潔に書くことができます。

var age = 30              // Go は自動的に 'age' を int 型と推論
var name = "田中太郎"      // Go は自動的に 'name' を string 型と推論
var isLoggedIn = true     // Go は自動的に 'isLoggedIn' を bool 型と推論

型を省略しても、Go は依然として強型付け(Strongly Typed)言語です。一度特定の型として推論された変数は、後から別の型の値を代入することはできません。これにより、簡潔さを保ちつつ、高い透明性と安全性を維持しています。

1.4 まとめて宣言(ブロック宣言)

var キーワードを使用すると、一つのステートメントで複数の変数を同時に宣言できます。これには主に2つの書き方があります。

1行での一括宣言:

var x, y int = 10, 20      // 2つの整数型変数 x と y を宣言し、それぞれ 10 と 20 を代入
var a, b = 100, "こんにちは" // 型推論を組み合わせ、異なる型の変数を同時に宣言・初期化

コードブロックでの一括宣言 (推奨):

var (
    firstName string = "John"
    lastName  string = "Doe"
    age       int    = 30
)

この「ブロック宣言」方式は Go 言語において非常に一般的であり、関連する変数をまとめることでコードの可読性を劇的に向上させます。

2. 短縮変数宣言::=

短縮変数宣言 := は、変数の宣言と初期化を極めて簡潔かつスピーディーに行う方法です。日常的な開発で最も頻繁に使用される構文ですが、いくつかの厳格なルールが存在します。

2.1 基本構文

短縮変数宣言の構文は非常に直感的です。

変数名 := 値

例:

age := 30              // 整数型変数 'age' を宣言して初期化
name := "田中太郎"      // 文字列型変数 'name' を宣言して初期化
isLoggedIn := true     // ブール型変数 'isLoggedIn' を宣言して初期化

:= を使用する場合、変数の型は必ず右側の値から自動的に推論されます。

2.2 核心的な制限ルール

:= は便利ですが、乱用は禁物です。以下の3つの制限を必ず覚えておいてください。

1. 関数内部でのみ使用可能: 短縮変数宣言 := は、パッケージレベル(関数の外側のグローバルスコープ)では絶対に使用できません。関数の外では var を使用する必要があります。

package main

// message := "Hello, World!" // エラー!コンパイル失敗。パッケージレベルでは := は使用不可

import "fmt"

func main() {
    message := "Hello, World!" // 正解:関数内部で使用
    fmt.Println(message)
}

2. 宣言と初期化を同時に含む必要がある::= 演算子は「宣言して代入する」ことを意味します。既に存在する変数に新しい値を再代入するために使用することはできません(再代入には = を使用します)。

3. 左側に少なくとも1つの「新規」変数が必要::= を使って複数の変数に同時に代入する場合、左辺の変数のうち少なくとも1つは、それまでに宣言されていない新しい変数である必要があります。

package main
import "fmt"

func main() {
    age := 30
    name := "田中太郎"

    age, newName := 40, "佐藤二朗" // OK: newName が全く新しい変数のため
    fmt.Println(age, newName)

    // age, name := 50, "鈴木花子" // エラー!コンパイル失敗。age と name は既に宣言されており、新しい変数がないため
}

3. 再宣言と変数シャドウイング

バグを避けるためには、スコープ(Scope)を理解することが不可欠です。

3.1 同一スコープ内での再宣言

上記のルール3で述べた通り、同一のスコープ内において := の左側に少なくとも1つの新しい変数が含まれていれば、既存の変数に対する「再宣言」が許可されます。これは単純な代入と混同されやすい点です。

package main
import "fmt"

func main() {
    age := 30
    fmt.Println("年齢:", age) // 出力: 年齢: 30
    
    age, name := 40, "田中太郎"   // 'age' は再宣言され、'name' は新規宣言
    fmt.Println("年齢:", age, "名前:", name) // 出力: 年齢: 40 名前: 田中太郎
    
    age = 50                 // これは単なる通常の代入(= を使用)
    fmt.Println("年齢:", age, "名前:", name) // 出力: 年齢: 50 名前: 田中太郎
}

3.2 変数シャドウイング (Shadowing)

内側のコードブロック(例えば if 文の中など)で、外側のブロックと同じ名前の変数を宣言すると、「変数シャドウイング(Shadowing)」が発生します。これは非常に混乱を招きやすく、予期せぬ挙動の原因となります。

package main
import "fmt"

func main() {
    age := 30 // 外側のスコープの変数

    if true {
        age := 40 // 内側のスコープの変数 (外側の 'age' をシャドウイング)
        fmt.Println("内部の年齢:", age) // 出力: 内部の年齢: 40
    }

    fmt.Println("外部の年齢:", age) // 出力: 外部の年齢: 30 (外側の age は書き換えられていない!)
}

この例では、if ブロック内部で := を使って宣言された age は、全く別の新しい変数です。内部の age に対する変更は、外部の 30 という値を持つ age には一切影響を与えません。

4. var と := のどちらを選ぶべきか?

実際の開発では、コンテキスト(文脈)と意図に基づいて選択すべきです。

var を優先すべきシーン:

  • 変数を宣言するが、すぐには初期値を与えない場合(Go のゼロ値メカニズムに依存する場合)。
  • 変数の型を明示的に指定したい場合(例: float64 を宣言したいが、初期値が 0 の場合)。
  • パッケージレベル(関数の外)でグローバル変数を宣言する場合。

:= を優先すべきシーン:

  • 関数内部で、ローカル変数を簡潔に宣言・初期化したい場合。
  • Go の型推論に任せて、冗長な入力を減らしたい場合。

比較まとめ表:

特性var キーワード:= 短縮宣言
主な機能変数の宣言(初期化は任意)変数の宣言と初期化を強制
初期化任意(未指定時はゼロ値)必須
型推論サポート(宣言時に値を代入する場合)常に初期値から推論
スコープパッケージおよび関数レベルで使用可能関数レベルでのみ使用可能
同一名での再宣言不可(= による代入のみ)許可(同一行に1つ以上の新変数が必要)

5. 総合コード例と練習

以下の実践的な例を通じて、理解を深めましょう。

5.1 例 1:var による変数宣言

package main
import "fmt"

func main() {
    var studentName string  // 学生の名前を保存する string 変数
    var studentAge int      // 学生の年齢を保存する int 変数
    var studentGPA float64  // 学生の評点 (GPA) を保存する float64 変数
    var isEnrolled bool     // 入学ステータスを記録する bool 変数

    // 後から代入
    studentName = "Alice Smith"
    studentAge = 20
    studentGPA = 3.85
    isEnrolled = true

    fmt.Println("学生氏名:", studentName)
    fmt.Println("年齢:", studentAge)
    fmt.Println("GPA:", studentGPA)
    fmt.Println("入学済み:", isEnrolled)
}

5.2 例 2::= による変数宣言

package main
import "fmt"

func main() {
    courseName := "Go言語基礎"     // 文字列変数を宣言・初期化
    courseDuration := 4          // 整数変数 (週数) を宣言・初期化
    isCompleted := false         // ブール変数を宣言・初期化

    fmt.Println("コース名:", courseName)
    fmt.Println("期間:", courseDuration, "週間")
    fmt.Println("修了済み:", isCompleted)
}