スコープ
スコープは、変数や定数などの識別子を参照できる範囲です。
スコープの種類
スコープは次の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
}
}