コンテンツにスキップ

Go/var

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go

varキーワードはGoにおいて変数宣言のために使用される基本的なキーワードです。この総合ガイドでは、varキーワードのすべての用途と他のキーワードとの組み合わせ、さまざまなユースケースについて詳しく説明します。

1. 基本的な変数宣言

[編集]

1.1 単一変数の宣言

[編集]
var name string
var age int
var salary float64
var isActive bool

最も基本的な形式として、varキーワードを使用して型を明示的に指定した変数を宣言できます。

1.2 初期値を持つ変数の宣言

[編集]
var name string = "田中太郎"
var age int = 30
var salary float64 = 45000.50
var isActive bool = true

宣言時に初期値を与えることができます。

1.3 型推論による変数宣言

[編集]
var name = "田中太郎"    // string型と推論される
var age = 30           // int型と推論される
var salary = 45000.50  // float64型と推論される
var isActive = true    // bool型と推論される

初期値を指定する場合、型を省略すると初期値から型が推論されます。

1.4 複数変数の宣言

[編集]
var firstName, lastName string
var width, height int
var x, y, z float64

同じ型の複数の変数を一度に宣言できます。

1.5 異なる型の複数変数の宣言

[編集]
var (
    name     string
    age      int
    salary   float64
    isActive bool
)

括弧を使用して、異なる型の複数の変数をグループ化して宣言できます。

1.6 複数変数の初期化

[編集]
var firstName, lastName = "太郎", "田中"
var width, height, depth = 10, 20, 15

複数の変数に同時に初期値を設定できます。

1.7 異なる型の複数変数の初期化

[編集]
var (
    name     = "田中太郎"
    age      = 30
    salary   = 45000.50
    isActive = true
)

括弧内で型推論を使用して複数の変数を初期化できます。

2. var と短縮変数宣言の比較

[編集]

2.1 短縮変数宣言(:=)

[編集]
name := "田中太郎"
age := 30
salary := 45000.50
isActive := true

関数内では、:=を使用したより簡潔な短縮変数宣言が一般的に使用されます。

2.2 varと短縮宣言の使い分け

[編集]
// パッケージレベルではvarが必要
var globalCounter int

func main() {
    // 関数内部では「:=」も使用可能
    localCounter := 0
    
    // 既存の変数に新しい値を代入する場合は<code>[[../=|=]]</code>
    localCounter = 1
}

varはパッケージレベルと関数内部の両方で使用できますが、:=は関数内部でのみ使用できます。

3. var キーワードとスコープ

[編集]

3.1 パッケージレベル(グローバル)変数

[編集]
package main

// パッケージレベル変数
var (
    maxConnections = 100
    timeout        = 30
    serviceName    = "UserAPI"
)

func main() {
    // パッケージレベル変数にアクセス
    println(serviceName, maxConnections)
}

varを使用してパッケージレベルの変数を宣言できます。

3.2 ブロックスコープ変数

[編集]
func processData() {
    var result string
    
    if data, err := getData(); err == nil {
        var temp = processRawData(data)
        result = formatData(temp)
    } else {
        // dataとtemp変数はここでは見えない
        result = "エラー発生"
    }
    
    // resultはこのスコープで利用可能
    return result
}

varで宣言された変数は、宣言されたブロック内でのみアクセス可能です。

4. var キーワードと型

[編集]

4.1 基本型の変数

[編集]
var i int
var u uint
var f float32
var s string
var b bool
var r rune
var by byte

基本的な組み込み型の変数を宣言できます。

4.2 複合型の変数

[編集]
var arr [5]int
var slice []int
var m map[string]int
var ch chan int
var ptr *int

配列、スライス、マップ、チャネル、ポインタなどの複合型の変数も宣言できます。

4.3 カスタム型の変数

[編集]
type Person struct {
    Name string
    Age  int
}

var person Person
var people []Person

typeキーワードで定義したカスタム型の変数を宣言できます。

4.4 インターフェース型の変数

[編集]
type Reader interface {
    Read(p []byte) (n int, err error)
}

var r Reader

インターフェース型の変数も宣言できます。

5. var キーワードと初期化

[編集]

5.1 ゼロ値による初期化

[編集]
var i int         // 0
var f float64     // 0.0
var b bool        // false
var s string      // ""
var p *int        // nil
var slice []int   // nil
var m map[string]int  // nil
var ch chan int   // nil

varで宣言された変数は自動的に型のゼロ値に初期化されます。

5.2 コンポジット・リテラルによる初期化

[編集]
var arr = [3]int{1, 2, 3}
var slice = []string{"a", "b", "c"}
var m = map[string]int{"one": 1, "two": 2}
var p = Person{Name: "田中太郎", Age: 30}

コンポジット・リテラルを使用して複合型変数を初期化できます。

5.3 初期化関数による初期化

[編集]
var complexData = initComplexData()

func initComplexData() map[string]interface{} {
    // 複雑な初期化ロジック
    result := make(map[string]interface{})
    // ... 初期化処理 ...
    return result
}

関数呼び出しの結果で変数を初期化できます。

6. var キーワードと他のキーワードの組み合わせ

[編集]

6.1 var と const の使い分け

[編集]
const pi = 3.14159    // 定数:変更不可
var ratio = 1.618     // 変数:変更可能

func updateRatio() {
    ratio = 1.62      // OK
    // pi = 3.14      // エラー:定数は再代入できない
}

変更が必要な値にはvarを、変更不要な値にはconstを使用します。

6.2 var と func の組み合わせ

[編集]
// 関数型の変数
var handler func(string) string

func init() {
    handler = func(s string) string {
        return "処理結果: " + s
    }
}

関数型の変数を宣言して、後で関数値を代入できます。

6.3 var と go (ゴルーチン) の組み合わせ

[編集]
var results = make(chan string, 10)

func processInBackground() {
    go func() {
        // バックグラウンド処理
        results <- "処理完了"
    }()
}

ゴルーチンでアクセスする変数を事前に宣言できます。

6.4 var と defer の組み合わせ

[編集]
func processFile(path string) error {
    var file *os.File
    var err error
    
    file, err = os.Open(path)
    if err != nil {
        return err
    }
    defer file.Close()
    
    // ファイル処理...
    return nil
}

deferで後処理が必要なリソースをvarで宣言できます。

6.5 var と type の組み合わせ

[編集]
var data struct {
    Name string
    Age  int
}

// 匿名構造体の変数を宣言して初期化
var config = struct {
    Host string
    Port int
}{
    Host: "localhost",
    Port: 8080,
}

typeキーワードを使わずに匿名構造体の変数を宣言できます。

6.6 var と range の組み合わせ

[編集]
var sum int
var numbers = []int{1, 2, 3, 4, 5}

func calculateSum() int {
    for _, num := range numbers {
        sum += num
    }
    return sum
}

rangeループで使用する変数や集計変数を事前に宣言できます。

6.7 var と switch の組み合わせ

[編集]
var dayType string
var weekday = time.Now().Weekday()

func getDayType() string {
    switch weekday {
    case time.Saturday, time.Sunday:
        dayType = "休日"
    default:
        dayType = "平日"
    }
    return dayType
}

switch文で使用する変数を事前に宣言できます。

6.8 var と if の組み合わせ

[編集]
var err error
var data []byte

func processIfValid() {
    data, err = ioutil.ReadFile("config.json")
    if err != nil {
        log.Fatal(err)
    }
    
    if len(data) > 0 {
        // データ処理...
    }
}

if文で使用する変数を事前に宣言できます。

7. var キーワードと特殊パターン

[編集]

7.1 nil の明示的代入

[編集]
var slice []int = nil
var m map[string]int = nil
var ch chan int = nil
var err error = nil
var ptr *int = nil

明示的にnilを代入できますが、通常は冗長と見なされます。

7.2 ブランク識別子(_)との組み合わせ

[編集]
var _, found = cache["key"]
var _, err = os.Stat("file.txt")

不要な戻り値を破棄するためにブランク識別子と組み合わせて使用できます。

7.3 シャドウイング(変数の隠蔽)

[編集]
var err error  // 外側のスコープのerr

func process() {
    if data, err := getValue(); err == nil {  // 内側のerrが外側のerrを隠す
        // ...
    }
    
    // ここでは外側のerrが見える
}

内側のスコープで同名の変数を宣言すると、外側の変数は一時的に隠されます。これは注意が必要なパターンです。

7.4 空白の変数宣言

[編集]
var names []string  // 空のスライス(nil)
var names = []string{}  // 空のスライス(非nil)

var m map[string]int  // 空のマップ(nil)
var m = map[string]int{}  // 空のマップ(非nil)

nilスライス/マップと空のスライス/マップには違いがあります。

8. 実践的なユースケース

[編集]

8.1 フラグ変数

[編集]
var (
    isDebug = false
    verbose = false
)

func init() {
    flag.BoolVar(&isDebug, "debug", false, "デバッグモードを有効にする")
    flag.BoolVar(&verbose, "verbose", false, "詳細な出力を有効にする")
    flag.Parse()
}

コマンドラインフラグとして使用する変数を宣言します。

8.2 グローバル設定

[編集]
var (
    config struct {
        Server struct {
            Host string
            Port int
        }
        Database struct {
            DSN string
        }
    }
)

func init() {
    // 設定ファイルからconfigを読み込む
    loadConfig()
}

アプリケーション全体で共有される設定を保持する変数を宣言します。

8.3 シングルトンパターン

[編集]
var (
    db *sql.DB
    dbOnce sync.Once
)

func GetDB() *sql.DB {
    dbOnce.Do(func() {
        var err error
        db, err = sql.Open("postgres", "connection-string")
        if err != nil {
            log.Fatal(err)
        }
    })
    return db
}

「sync.Once」と組み合わせて、遅延初期化されるシングルトンパターンを実装できます。

8.4 依存関係の注入

[編集]
var (
    userRepo  UserRepository
    authService AuthService
)

func SetupDependencies(repo UserRepository, auth AuthService) {
    userRepo = repo
    authService = auth
}

テストや設定変更が容易になるよう、依存関係を外部から注入できる変数を宣言します。

8.5 キャッシュ

[編集]
var (
    cache = make(map[string]interface{})
    cacheMutex sync.RWMutex
)

func GetFromCache(key string) (interface{}, bool) {
    cacheMutex.RLock()
    defer cacheMutex.RUnlock()
    val, found := cache[key]
    return val, found
}

func SetToCache(key string, value interface{}) {
    cacheMutex.Lock()
    defer cacheMutex.Unlock()
    cache[key] = value
}

アプリケーション全体で共有されるキャッシュを宣言します。

8.6 カウンターや統計

[編集]
var (
    requestCount int64
    errorCount   int64
)

func incrementRequestCount() int64 {
    return atomic.AddInt64(&requestCount, 1)
}

func incrementErrorCount() int64 {
    return atomic.AddInt64(&errorCount, 1)
}

アプリケーションの統計情報を保持する原子的に更新される変数を宣言します。

8.7 環境変数

[編集]
var (
    environment string
    port        string
)

func init() {
    environment = os.Getenv("ENV")
    if environment == "" {
        environment = "development"
    }
    
    port = os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
}

環境変数から設定を読み込む変数を宣言します。

8.8 定数のようなパッケージレベル変数

[編集]
var (
    // これらは事実上定数だが、計算結果なので定数として宣言できない
    MaxSafeInteger = int64(1)<<53 - 1
    DefaultTimeout = 30 * time.Second
)

計算が必要でconstとして宣言できない「定数同様の値」を定義できます。

8.9 スレッドセーフな初期化

[編集]
var (
    config     *Config
    configLock sync.RWMutex
)

func GetConfig() *Config {
    configLock.RLock()
    if config != nil {
        defer configLock.RUnlock()
        return config
    }
    configLock.RUnlock()
    
    configLock.Lock()
    defer configLock.Unlock()
    if config == nil {
        config = loadConfigFromFile()
    }
    return config
}

ミューテックスを使用して、スレッドセーフな遅延初期化を実装できます。

8.10 テスト用の変数

[編集]
var (
    // テスト時に差し替え可能
    timeNow = time.Now
    randInt = rand.Int
)

func generateID() string {
    now := timeNow()
    random := randInt()
    return fmt.Sprintf("%d-%d", now.Unix(), random)
}

テスト時に差し替え可能な関数変数を宣言します。

9. var と型のユースケース

[編集]

9.1 インターフェースの実装確認

[編集]
var _ io.Reader = (*MyReader)(nil)
var _ io.Writer = (*MyWriter)(nil)

コンパイル時にインターフェースの実装を確認するパターンです。

9.2 埋め込みインターフェースと構造体

[編集]
type Handler struct {
    // 埋め込みインターフェース
    var http.Handler
    
    // 追加フィールド
    Logger *log.Logger
}

構造体内にインターフェースを埋め込むことができます(通常はキーワードなしで書きます)。

9.3 関数型変数

[編集]
var (
    // HTTPハンドラー関数の型
    homeHandler = func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Home Page")
    }
    
    // 計算関数の型
    calculator = func(a, b int) int {
        return a + b
    }
)

関数型の変数を宣言して関数値を代入できます。

10. var と並行処理パターン

[編集]

10.1 ゴルーチンの通信チャネル

[編集]
var (
    tasks   = make(chan Task, 100)
    results = make(chan Result, 100)
    errors  = make(chan error, 100)
    done    = make(chan struct{})
)

func startWorkers(n int) {
    for i := 0; i < n; i++ {
        go worker(tasks, results, errors)
    }
}

ゴルーチン間の通信に使用するチャネルを宣言します。

10.2 コンテキスト変数

[編集]
var (
    rootCtx       context.Context
    rootCtxCancel context.CancelFunc
)

func init() {
    rootCtx, rootCtxCancel = context.WithCancel(context.Background())
}

func cleanup() {
    rootCtxCancel()
}

アプリケーション全体で使用するルートコンテキストを宣言します。

10.3 同期プリミティブ

[編集]
var (
    mu      sync.Mutex
    rwmu    sync.RWMutex
    once    sync.Once
    wg      sync.WaitGroup
    cond    = sync.NewCond(&sync.Mutex{})
)

並行処理のための同期プリミティブを宣言します。

10.4 アトミック変数

[編集]
var (
    counter    int64
    isShutdown int32
)

func increment() int64 {
    return atomic.AddInt64(&counter, 1)
}

func shutdown() {
    atomic.StoreInt32(&isShutdown, 1)
}

func isActive() bool {
    return atomic.LoadInt32(&isShutdown) == 0
}

アトミックな操作が必要な変数を宣言します。

まとめ

[編集]

Goのvarキーワードは変数宣言のための基本的なキーワードであり、さまざまな状況で使用されます:

  1. 基本的な変数宣言(型指定あり・なし)
  2. パッケージレベルとブロックスコープでの宣言
  3. 様々な型(基本型、複合型、カスタム型)の変数宣言
  4. 他のキーワード(const, func, go, defer, type, range, switch, if)との組み合わせ
  5. 特殊パターン(シャドウイング、ブランク識別子、nil代入)
  6. 実践的なユースケース(フラグ変数、グローバル設定、シングルトン、依存関係注入、キャッシュ)
  7. 型に関連するユースケース(インターフェース実装確認、関数型変数)
  8. 並行処理パターン(チャネル、コンテキスト、同期プリミティブ、アトミック変数)

varキーワードの適切な使用法を理解することで、より読みやすく保守性の高いGoプログラムを書くことができます。varは単なる変数宣言の手段以上のものであり、Goの設計哲学とイディオムを反映しています。適切なコンテキストでvarを使用するか短縮宣言(:=)を使用するかを理解することは、効果的なGoプログラミングの重要な側面です。