スコープ

スコープは、変数や定数などの識別子を参照できる範囲です。

スコープの種類

スコープは次の4つに分類できます。

  • ユニバース
  • パッケージ
  • ファイル
  • ローカル

ユニバース

すべてのソースで有効なものです。 事前に宣言済みの識別子が該当します。

  • 型(bool/int/stringなど)
  • 定数(true/false/iota)
  • ゼロ値(nil)
  • 関数(len/make/newなど)

パッケージ

パッケージの中で有効なものです。 対象の識別子は型・変数・定数・関数が該当します。

package main

import (
    "fmt"
)

// 定数
const (
    c1 = 10
)

// 変数
var (
    v1 = 1
)

// 型
type myint int

// 関数
func main() {
    fmt.Println(v1, c1)
    fmt.Println(myint(1))
}

ファイル

ファイルの中で有効なものです。 対象の識別子はインポートされたパッケージが該当します。

import (
    "fmt"
)

ブロック

波括弧の中で有効なものです。 対象の識別子は型・定数・変数・ラベルが該当します。

func main() {
    // 型
    type myint int
    // 定数
    const c = myint(1)
    // 変数
    v := 1
    fmt.Println(c, v)
    // ラベル
L:
    for i := range []int{1} {
        for j := range []int{1} {
            fmt.Println(i, j)
            break L
        }
    }
}

ブロックは関数やフロー制御(if文・switch文・for文)を書いた際に作られますが、 意図的にブロックを作ることもできます。

func main() {
    v1 := 1
    // 意図的に作ったブロック
    {
        v2 := 2
        fmt.Println(v2) // 2
    }
    fmt.Println(v1) // 1
}

識別子のシャドウイング

ブロックが異なれば同名の識別子を使えます。 この場合、内側のブロックの識別子が参照されます。

次のコードは識別子のシャドウイングを確認するためのものです。 このような読みづらいコードはあえて書かないでしょう。 とはいえ、意図せず変数名が被ってしまい、想定しない動作をすることがあるので注意してください。

const v = 1

func main() {
    fmt.Println(v) // 1
    v := v + 1
    fmt.Println(v) // 2
    if v := v + 1; v == 3 {
        fmt.Println(v) // 3
        v := v + 1
        fmt.Println(v) // 4
    }
}