Go言語入門:言語仕様 -Vol.9-

スポンサーリンク
Go言語入門:言語仕様 -Vol.9- ノウハウ
Go言語入門:言語仕様 -Vol.9-
この記事は約15分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、Go言語の言語仕様について解説しています。

スポンサーリンク

背景

Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。

言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。

そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。

言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!

Identifiers(識別子)

識別子は、変数や型などのプログラム要素に名前を付けます。識別子は、1文字以上の文字と数字の列です。識別子の最初の文字は、文字でなければなりません。

identifier = letter { letter | unicode_digit } .
a
_x9
ThisVariableIsExported
αβ

一部の識別子は事前宣言されています。


解説

識別子とは何か?

識別子(identifier) は、プログラムの中で「名前」として使われる文字列です。変数、関数、型、パッケージなど、あらゆるものに名前を付けるときに使います。

たとえ話: 人に名前があるように、プログラムの中の要素にも名前が必要です。「太郎さん」「花子さん」と呼ぶように、変数にも「userName」「age」といった名前を付けます。

package main

import "fmt"

func main() {
    // これらはすべて識別子
    var userName string = "太郎"   // userName が識別子
    var age int = 25               // age が識別子
    var isAdult bool = true        // isAdult が識別子
    
    calculateTotal(100, 200)       // calculateTotal が識別子
    
    fmt.Println(userName, age, isAdult)  // Println も識別子
}

// calculateTotal も識別子
func calculateTotal(price1, price2 int) int {  // price1, price2 も識別子
    return price1 + price2
}

識別子の構文ルール

識別子は以下のルールに従って作られます:

identifier = letter { letter | unicode_digit } .

これを分解すると:

  1. 最初は必ず文字(letter)
    • letter = アルファベット、日本語、ギリシャ文字、アンダースコア(_)など
  2. 2文字目以降は文字または数字
    • letter または unicode_digit(0-9など)を何文字でも

ルール1: 最初の文字は必ず「文字」

package main

func main() {
    // ✅ 正しい例
    var name string        // 英字で始まる
    var _temp int          // アンダースコアで始まる
    var 名前 string         // 日本語で始まる
    var α float64          // ギリシャ文字で始まる
    
    // ❌ 間違った例
    // var 1name string    // エラー! 数字で始まっている
    // var 9lives int      // エラー! 数字で始まっている
}

なぜ数字で始められないの?

数字で始まると、数値リテラル(例: 123)と区別がつかなくなるためです。

// もし数字で始められたら...
// var 123 int = 456  // これは何を意味する? 123という変数? それとも数値?

ルール2: 2文字目以降は文字または数字

package main

func main() {
    // ✅ すべて正しい
    var name1 string        // 文字 + 数字
    var user_name string    // 文字 + アンダースコア + 文字
    var test123 int         // 文字 + 数字の連続
    var 変数2 int            // 日本語 + 数字
    var _123 int            // アンダースコア + 数字
    var αβγ123 float64      // ギリシャ文字 + 数字
}

識別子の具体例

仕様書に挙げられている例を見てみましょう:

例1: a

package main

func main() {
    var a int = 10  // 1文字だけの識別子も有効
    
    // 短い識別子はループ変数などでよく使う
    for i := 0; i < 10; i++ {  // i, j, k などもよく使われる
        // ...
    }
}

使用場面:

  • ループのカウンタ変数(i, j, k)
  • 一時的な変数
  • 数学的な変数(x, y, z)

例2: _x9

package main

func main() {
    var _x9 int = 42  // アンダースコアで始まり、数字を含む
    
    // アンダースコアで始まる識別子は、
    // 内部的な変数やプライベートな変数によく使われる
    var _temp string = "一時データ"
    var _cache map[string]int = make(map[string]int)
}

使用場面:

  • 内部使用の変数(パブリックにしたくない)
  • 一時的な変数
  • テストコード

特殊なアンダースコア _:

package main

import "fmt"

func getValues() (int, string) {
    return 42, "太郎"
}

func main() {
    // アンダースコアだけの識別子は「空白識別子」
    // 値を無視したいときに使う
    value, _ := getValues()  // 2番目の戻り値を無視
    fmt.Println(value)
    
    // 複数の値を無視することも可能
    _, name := getValues()   // 1番目の戻り値を無視
    fmt.Println(name)
}

例3: ThisVariableIsExported

package main

import "fmt"

// 大文字で始まる識別子は「エクスポートされる」(公開される)
var ThisVariableIsExported string = "公開変数"

// 小文字で始まる識別子は「エクスポートされない」(非公開)
var thisVariableIsNotExported string = "非公開変数"

func main() {
    fmt.Println(ThisVariableIsExported)
}

重要な慣習: Goでは、大文字で始まるか小文字で始まるかで公開範囲が変わります

先頭文字公開範囲
大文字パッケージ外からアクセス可能(public)UserName, GetValue()
小文字パッケージ内のみアクセス可能(private)userName, getValue()
package mypackage

// Public: 他のパッケージから使える
var PublicVariable int = 100
func PublicFunction() {}
type PublicStruct struct {}

// Private: このパッケージ内でのみ使える
var privateVariable int = 200
func privateFunction() {}
type privateStruct struct {}

命名規則(CamelCase):

Goでは、複数の単語を組み合わせるときにキャメルケース(らくだのこぶのように大文字小文字が交互に来る)を使うのが一般的です。

package main

// ✅ 推奨される命名(CamelCase)
var userName string           // 先頭小文字 = private
var UserName string           // 先頭大文字 = public
var maxConnectionCount int    // 複数単語をつなげる
var HTTPServer string         // 略語は大文字で統一

// ❌ Goではあまり使わない命名(snake_case)
// var user_name string       // 他の言語では一般的だが、Goでは非推奨
// var max_connection_count int

例4: αβ

package main

import "fmt"

func main() {
    // ギリシャ文字も識別子として使える
    var α float64 = 3.14159  // alpha
    var β float64 = 2.71828  // beta
    var γ float64 = 1.61803  // gamma
    
    // 数学的なコードで便利
    var π float64 = 3.14159
    var Δ float64 = 0.001  // delta(変化量)
    var Σ int              // sigma(合計)
    
    fmt.Println(α, β, γ, π, Δ, Σ)
}

使用場面:

  • 科学計算、数学的なコード
  • 物理シミュレーション
  • 統計処理

注意点: ギリシャ文字は便利ですが、チーム開発では入力しやすさや読みやすさも考慮しましょう。

事前宣言された識別子(Predeclared identifiers)

Goには、最初から定義されている識別子があります。これらは特別なインポートなしで使えます。

package main

func main() {
    // 事前宣言された型
    var b bool          // 真偽値
    var i int           // 整数
    var i8 int8         // 8ビット整数
    var i16 int16       // 16ビット整数
    var i32 int32       // 32ビット整数
    var i64 int64       // 64ビット整数
    var u uint          // 符号なし整数
    var u8 uint8        // 8ビット符号なし整数
    var u16 uint16      // 16ビット符号なし整数
    var u32 uint32      // 32ビット符号なし整数
    var u64 uint64      // 64ビット符号なし整数
    var f32 float32     // 32ビット浮動小数点
    var f64 float64     // 64ビット浮動小数点
    var c64 complex64   // 64ビット複素数
    var c128 complex128 // 128ビット複素数
    var s string        // 文字列
    var r rune          // ルーン(= int32)
    var bt byte         // バイト(= uint8)
    
    // 特殊な型
    var e error         // エラー型
}

定数

package main

import "fmt"

func main() {
    // 事前宣言された定数
    var t bool = true   // 真
    var f bool = false  // 偽
    
    // iota(連続した整数を生成)
    const (
        Sunday = iota  // 0
        Monday         // 1
        Tuesday        // 2
    )
    
    fmt.Println(t, f, Sunday, Monday, Tuesday)
}

ゼロ値

package main

func main() {
    // nil: ゼロ値(ポインタ、スライス、マップ、チャネルなど)
    var ptr *int = nil
    var slice []int = nil
    var m map[string]int = nil
}

組み込み関数

package main

import "fmt"

func main() {
    // 事前宣言された関数
    
    // make: スライス、マップ、チャネルを作成
    slice := make([]int, 5)
    m := make(map[string]int)
    
    // new: ポインタを作成
    ptr := new(int)
    
    // len: 長さを取得
    length := len(slice)
    
    // cap: 容量を取得
    capacity := cap(slice)
    
    // append: スライスに要素を追加
    slice = append(slice, 10)
    
    // copy: スライスをコピー
    copied := make([]int, len(slice))
    copy(copied, slice)
    
    // delete: マップから要素を削除
    delete(m, "key")
    
    // panic: パニックを発生させる
    // panic("エラーが発生しました")
    
    // recover: パニックから回復
    // defer func() {
    //     if r := recover(); r != nil {
    //         fmt.Println("回復しました:", r)
    //     }
    // }()
    
    // close: チャネルを閉じる
    ch := make(chan int)
    close(ch)
    
    // print, println: デバッグ用(非推奨、fmtパッケージを使う)
    println("デバッグメッセージ")
    
    fmt.Println(length, capacity, ptr, copied)
}

注意: これらの事前宣言された識別子は、自分で再定義できます(ただし非推奨)。

package main

import "fmt"

func main() {
    // ❌ 悪い例: 事前宣言された識別子を再定義
    var true int = 42  // コンパイルは通るが非常に混乱する!
    fmt.Println(true)  // 42 が出力される
    
    // これは絶対に避けるべき
}

識別子の命名規則とベストプラクティス

推奨される命名スタイル

package main

// ✅ 良い命名例

// 変数: キャメルケース、意味のある名前
var userName string
var maxRetryCount int
var isAuthenticated bool

// 関数: キャメルケース、動詞で始める
func getUserName() string { return "" }
func calculateTotal(items []int) int { return 0 }
func isValid(input string) bool { return true }

// 型: キャメルケース、名詞
type User struct {
    Name string
    Age  int
}

type HTTPClient struct {  // 略語は大文字で統一
    // ...
}

// 定数: キャメルケースまたは全部大文字
const MaxConnections = 100
const APIEndpoint = "https://api.example.com"

// パッケージレベルの変数: 説明的な名前
var DefaultTimeout = 30
var ErrNotFound = errors.New("not found")

避けるべき命名

package main

// ❌ 悪い命名例

// 意味不明な略語
var usrnm string  // userNameの方が良い
var cnt int       // countの方が良い

// ハンガリアン記法(型を名前に含める)
var strName string   // Goでは不要
var intAge int       // 型はコンパイラが知っている

// スネークケース(Goの慣習ではない)
var user_name string      // userNameの方が良い
var max_retry_count int   // maxRetryCountの方が良い

// 長すぎる名前
var theNumberOfItemsInTheShoppingCart int  // itemCountなどで十分

// 短すぎる名前(スコープが広い場合)
var x string  // グローバル変数には不適切

スコープに応じた命名

package main

func main() {
    // ✅ 短いスコープでは短い名前でOK
    for i := 0; i < 10; i++ {  // i は数行で終わるのでOK
        // ...
    }
    
    // ✅ 長いスコープでは説明的な名前
    var userSessionTimeout int = 3600  // グローバルレベルでは詳しく
}

まとめ: 識別子で覚えておくべきこと

  1. 識別子は文字で始まる(数字で始められない)
  2. 2文字目以降は文字または数字
  3. アンダースコア_は文字として扱われる
  4. 大文字で始まる = 公開(public)、小文字で始まる = 非公開(private)
  5. キャメルケースが推奨(スネークケースは非推奨)
  6. 事前宣言された識別子がある(型、定数、関数など)
  7. 日本語やギリシャ文字も使える(ただしチーム規約に従う)

実用的なアドバイス:

package main

import "fmt"

// 公開する構造体(大文字で始まる)
type User struct {
    Name string  // 公開フィールド
    age  int     // 非公開フィールド
}

// 公開する関数(大文字で始まる)
func GetUserName(u User) string {
    return u.Name
}

// 非公開の関数(小文字で始まる)
func calculateAge(birthYear int) int {
    currentYear := 2024
    return currentYear - birthYear
}

func main() {
    // 意味のある変数名を使う
    user := User{Name: "太郎", age: 25}
    
    // 短いスコープでは短い名前もOK
    for i := 0; i < 3; i++ {
        fmt.Println(i)
    }
    
    fmt.Println(GetUserName(user))
}

良い識別子の命名は、読みやすく、保守しやすいコードを書くための第一歩です!

おわりに 

本日は、Go言語の言語仕様について解説しました。

よっしー
よっしー

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました