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

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

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

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

スポンサーリンク

背景

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

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

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

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

Keywords(キーワード)

以下のキーワードは予約されており、識別子として使用することはできません。

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

解説

キーワードとは何か?

キーワード(予約語) は、Go言語で特別な意味を持つ単語です。これらは言語の文法として組み込まれているため、変数名や関数名として使うことができません

たとえ話: 日本語の「です」「ます」「が」「を」といった助詞や助動詞のように、文法上の役割が決まっている言葉です。これらを人の名前として使えないのと同じように、キーワードは識別子として使えません。

package main

func main() {
    // ❌ エラー! キーワードは識別子として使えない
    // var if int = 10
    // var func string = "test"
    // var return bool = true
    
    // ✅ 正しい
    var myIf int = 10
    var myFunc string = "test"
    var returnValue bool = true
}

Goの25個のキーワード

Goにはたった25個のキーワードしかありません(他の言語と比べて非常に少ない)。これはGoが「シンプルで覚えやすい」ことを重視しているからです。

参考:

  • C++: 約90個
  • Java: 約50個
  • Python: 約35個
  • Go: 25個

これらを用途別に分類して見ていきましょう。


1. プログラム構造に関するキーワード(5個)

package

パッケージ宣言に使います。すべてのGoファイルは必ずpackage宣言で始まります。

package main  // 実行可能プログラムのエントリーポイント

package utils  // ライブラリパッケージ

package myapp  // カスタムパッケージ

用途: コードを論理的なまとまりに整理する

import

他のパッケージをインポートするときに使います。

package main

import "fmt"  // 単一インポート

import (
    "fmt"      // 複数インポート
    "os"
    "net/http"
)

用途: 標準ライブラリや外部ライブラリの機能を使う

func

関数を定義するときに使います。

package main

import "fmt"

// 通常の関数
func greet(name string) {
    fmt.Printf("こんにちは、%sさん\n", name)
}

// 戻り値がある関数
func add(a, b int) int {
    return a + b
}

// 複数の戻り値
func getValues() (int, string) {
    return 42, "太郎"
}

func main() {
    greet("花子")
    sum := add(10, 20)
    fmt.Println(sum)
}

用途: 再利用可能なコードブロックを作成

type

新しい型を定義するときに使います。

package main

import "fmt"

// 構造体の定義
type User struct {
    Name string
    Age  int
}

// 型エイリアス
type UserID int

// インターフェースの定義
type Writer interface {
    Write(data []byte) (int, error)
}

func main() {
    user := User{Name: "太郎", Age: 25}
    var id UserID = 12345
    fmt.Println(user, id)
}

用途: カスタム型、構造体、インターフェースの定義

struct

構造体を定義するときに使います(typeと組み合わせて使う)。

package main

import "fmt"

// 構造体の定義
type Person struct {
    Name    string
    Age     int
    Address string
}

// 埋め込み構造体
type Employee struct {
    Person       // Personを埋め込む
    EmployeeID int
    Department string
}

func main() {
    person := Person{
        Name:    "太郎",
        Age:     30,
        Address: "東京",
    }
    
    employee := Employee{
        Person: Person{
            Name: "花子",
            Age:  28,
        },
        EmployeeID: 12345,
        Department: "営業",
    }
    
    fmt.Println(person)
    fmt.Println(employee)
}

用途: 複数のフィールドをまとめたデータ構造を作成


2. 変数・定数宣言に関するキーワード(2個)

var

変数を宣言するときに使います。

package main

import "fmt"

func main() {
    // 基本的な宣言
    var name string = "太郎"
    
    // 型推論
    var age = 25  // intと推論される
    
    // ゼロ値で初期化
    var count int  // 0で初期化される
    
    // 複数同時宣言
    var x, y, z int = 1, 2, 3
    
    // ブロックでまとめて宣言
    var (
        firstName string = "太郎"
        lastName  string = "山田"
        userAge   int    = 30
    )
    
    fmt.Println(name, age, count, x, y, z)
    fmt.Println(firstName, lastName, userAge)
}

短縮構文(:=)との違い:

package main

func main() {
    // var を使った宣言
    var name string = "太郎"
    
    // 短縮構文(関数内のみ)
    age := 25
    
    // パッケージレベルではvarが必要
    // packageLevel := 100  // エラー! パッケージレベルでは := 使えない
}

var packageLevel int = 100  // OK

const

定数を宣言するときに使います。定数は値を変更できません。

package main

import "fmt"

// 単一の定数
const Pi = 3.14159

// 複数の定数
const (
    StatusOK       = 200
    StatusNotFound = 404
    StatusError    = 500
)

// iotaを使った連番
const (
    Sunday = iota  // 0
    Monday         // 1
    Tuesday        // 2
    Wednesday      // 3
)

func main() {
    const localConst = "ローカル定数"
    
    // エラー! 定数は変更できない
    // Pi = 3.14  // コンパイルエラー
    
    fmt.Println(Pi, StatusOK, Sunday, localConst)
}

用途: 変更されない値(設定値、ステータスコード、数学定数など)


3. 制御フロー(条件分岐)に関するキーワード(6個)

if、else

条件分岐を行います。

package main

import "fmt"

func main() {
    age := 20
    
    // 基本的なif文
    if age >= 20 {
        fmt.Println("成人です")
    }
    
    // if-else
    if age >= 20 {
        fmt.Println("成人です")
    } else {
        fmt.Println("未成年です")
    }
    
    // if-else if-else
    score := 85
    if score >= 90 {
        fmt.Println("優秀")
    } else if score >= 70 {
        fmt.Println("良好")
    } else {
        fmt.Println("要努力")
    }
    
    // 短縮構文(変数宣言 + 条件)
    if num := getNumber(); num > 0 {
        fmt.Println("正の数:", num)
    } else {
        fmt.Println("0または負の数:", num)
    }
}

func getNumber() int {
    return 42
}

switch、case、default、fallthrough

複数の条件から選択します。

package main

import "fmt"

func main() {
    // 基本的なswitch
    day := "月曜日"
    switch day {
    case "月曜日":
        fmt.Println("週の始まり")
    case "金曜日":
        fmt.Println("週末が近い")
    case "土曜日", "日曜日":  // 複数の値
        fmt.Println("週末です")
    default:
        fmt.Println("平日です")
    }
    
    // 式を持たないswitch(if-else if-elseの代替)
    score := 85
    switch {
    case score >= 90:
        fmt.Println("A")
    case score >= 80:
        fmt.Println("B")
    case score >= 70:
        fmt.Println("C")
    default:
        fmt.Println("D")
    }
    
    // fallthroughの使用(次のcaseも実行)
    num := 2
    switch num {
    case 1:
        fmt.Println("1")
        fallthrough  // 次のcaseも実行
    case 2:
        fmt.Println("2")
        fallthrough
    case 3:
        fmt.Println("3")
    }
    // 出力: 2, 3
}

注意: Goのswitchは、C言語と違って自動的にbreakされます。次のcaseも実行したい場合のみfallthroughを使います。


4. ループに関するキーワード(4個)

for

繰り返し処理を行います。Goにはforしかループがありません(whileやdo-whileはない)。

package main

import "fmt"

func main() {
    // 基本的なfor(C言語スタイル)
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
    
    // while風のfor
    count := 0
    for count < 5 {
        fmt.Println(count)
        count++
    }
    
    // 無限ループ
    // for {
    //     fmt.Println("無限ループ")
    //     break  // breakで抜ける
    // }
    
    // rangeを使ったループ
    numbers := []int{10, 20, 30}
    for index, value := range numbers {
        fmt.Printf("numbers[%d] = %d\n", index, value)
    }
    
    // インデックスだけ使う
    for index := range numbers {
        fmt.Println(index)
    }
    
    // 値だけ使う
    for _, value := range numbers {
        fmt.Println(value)
    }
}

range

配列、スライス、マップ、チャネルを反復処理します。

package main

import "fmt"

func main() {
    // スライスのrange
    fruits := []string{"りんご", "バナナ", "みかん"}
    for i, fruit := range fruits {
        fmt.Printf("%d: %s\n", i, fruit)
    }
    
    // マップのrange
    prices := map[string]int{
        "りんご": 100,
        "バナナ": 80,
    }
    for name, price := range prices {
        fmt.Printf("%s: %d円\n", name, price)
    }
    
    // 文字列のrange(ルーン単位)
    text := "こんにちは"
    for i, char := range text {
        fmt.Printf("%d: %c\n", i, char)
    }
}

break

ループを抜けるときに使います。

package main

import "fmt"

func main() {
    // 条件でループを抜ける
    for i := 0; i < 10; i++ {
        if i == 5 {
            break  // i==5でループ終了
        }
        fmt.Println(i)
    }
    // 出力: 0, 1, 2, 3, 4
    
    // ラベル付きbreak(ネストループを抜ける)
OuterLoop:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if i == 1 && j == 1 {
                break OuterLoop  // 外側のループも抜ける
            }
            fmt.Printf("i=%d, j=%d\n", i, j)
        }
    }
}

continue

現在の反復をスキップして次の反復に進みます。

package main

import "fmt"

func main() {
    // 偶数だけ出力(奇数をスキップ)
    for i := 0; i < 10; i++ {
        if i%2 != 0 {
            continue  // 奇数はスキップ
        }
        fmt.Println(i)
    }
    // 出力: 0, 2, 4, 6, 8
    
    // ラベル付きcontinue
OuterLoop:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if j == 1 {
                continue OuterLoop  // 外側ループの次の反復へ
            }
            fmt.Printf("i=%d, j=%d\n", i, j)
        }
    }
}

5. 関数に関するキーワード(3個)

return

関数から値を返す、または関数を終了します。

package main

import "fmt"

// 値を返す
func add(a, b int) int {
    return a + b
}

// 複数の値を返す
func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("0で割れません")
    }
    return a / b, nil
}

// 名前付き戻り値
func calculate(a, b int) (sum, diff int) {
    sum = a + b
    diff = a - b
    return  // 名前付き戻り値は自動的に返される
}

// 値を返さない関数
func printMessage() {
    fmt.Println("メッセージ")
    return  // 省略可能
}

func main() {
    result := add(10, 20)
    fmt.Println(result)
    
    quotient, err := divide(10, 2)
    if err != nil {
        fmt.Println("エラー:", err)
    } else {
        fmt.Println(quotient)
    }
    
    s, d := calculate(10, 5)
    fmt.Println(s, d)
}

defer

関数の終了時に実行される処理を登録します。

package main

import "fmt"

func main() {
    // deferは関数終了時に実行される(LIFO: 後入れ先出し)
    defer fmt.Println("3番目に実行")
    defer fmt.Println("2番目に実行")
    defer fmt.Println("1番目に実行")
    
    fmt.Println("通常の処理")
    
    // 出力:
    // 通常の処理
    // 1番目に実行
    // 2番目に実行
    // 3番目に実行
}

// 実用例: リソースのクリーンアップ
func readFile() {
    file, err := os.Open("test.txt")
    if err != nil {
        return
    }
    defer file.Close()  // 関数終了時に必ずファイルを閉じる
    
    // ファイル読み込み処理
    // ...
}

go

ゴルーチン(並行処理)を開始します。

package main

import (
    "fmt"
    "time"
)

func sayHello(name string) {
    fmt.Printf("こんにちは、%sさん\n", name)
}

func main() {
    // 通常の関数呼び出し
    sayHello("太郎")
    
    // ゴルーチンで並行実行
    go sayHello("花子")
    go sayHello("次郎")
    
    // メインゴルーチンが終了しないように待つ
    time.Sleep(1 * time.Second)
    
    // 無名関数をゴルーチンで実行
    go func() {
        fmt.Println("無名関数から")
    }()
    
    time.Sleep(1 * time.Second)
}

6. 並行処理に関するキーワード(3個)

chan

チャネル(データをやり取りする通路)を定義します。

package main

import "fmt"

func main() {
    // チャネルの作成
    ch := make(chan int)
    
    // ゴルーチンでデータを送信
    go func() {
        ch <- 42  // チャネルに送信
    }()
    
    // チャネルから受信
    value := <-ch
    fmt.Println(value)  // 42
    
    // バッファ付きチャネル
    bufferedCh := make(chan string, 2)
    bufferedCh <- "Hello"
    bufferedCh <- "World"
    fmt.Println(<-bufferedCh)  // Hello
    fmt.Println(<-bufferedCh)  // World
}

select

複数のチャネル操作から1つを選択します。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "from ch1"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "from ch2"
    }()
    
    // 最初に準備ができたチャネルから受信
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    case <-time.After(3 * time.Second):
        fmt.Println("タイムアウト")
    }
}

7. その他のキーワード(2個)

goto

指定したラベルにジャンプします(使用は非推奨)。

package main

import "fmt"

func main() {
    i := 0
    
Loop:
    fmt.Println(i)
    i++
    
    if i < 5 {
        goto Loop  // Loopラベルにジャンプ
    }
    
    fmt.Println("終了")
}

注意: gotoは可読性を損なうため、できるだけ使わないことが推奨されます。

interface

インターフェース(メソッドの集合)を定義します。

package main

import "fmt"

// インターフェースの定義
type Shape interface {
    Area() float64
    Perimeter() float64
}

// 構造体
type Rectangle struct {
    Width  float64
    Height float64
}

// Rectangleがshapeインターフェースを実装
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

func main() {
    var s Shape = Rectangle{Width: 10, Height: 5}
    fmt.Println("面積:", s.Area())
    fmt.Println("周囲:", s.Perimeter())
}

8. その他の型関連キーワード(1個)

map

マップ(連想配列)を定義します。

package main

import "fmt"

func main() {
    // マップの作成
    prices := make(map[string]int)
    prices["りんご"] = 100
    prices["バナナ"] = 80
    
    // リテラルでの初期化
    ages := map[string]int{
        "太郎": 25,
        "花子": 30,
    }
    
    // 値の取得
    price := prices["りんご"]
    fmt.Println(price)  // 100
    
    // 存在チェック
    age, exists := ages["太郎"]
    if exists {
        fmt.Println("年齢:", age)
    }
    
    // 削除
    delete(prices, "りんご")
    
    // イテレーション
    for name, age := range ages {
        fmt.Printf("%s: %d歳\n", name, age)
    }
}

まとめ: キーワードで覚えておくべきこと

キーワードの分類表

分類キーワード
プログラム構造package, import, func, type, struct
変数・定数var, const
条件分岐if, else, switch, case, default, fallthrough
ループfor, range, break, continue
関数return, defer, go
並行処理chan, select
その他goto, interface, map

重要なポイント

  1. キーワードは識別子として使えない
  2. Goには25個のキーワードしかない(シンプル!)
  3. 大文字・小文字を変えても使えない(Ifもエラー)
  4. 最もよく使うのは: package, import, func, var, if, for, return

実用的なアドバイス

package main  // package

import "fmt"  // import

type User struct {  // type, struct
    Name string
}

func main() {  // func
    var count int = 0  // var
    const Max = 100    // const
    
    for i := 0; i < 10; i++ {  // for
        if i%2 == 0 {  // if
            count++
        } else {  // else
            continue  // continue
        }
    }
    
    result := make(map[string]int)  // map
    ch := make(chan int)            // chan
    
    defer fmt.Println("終了")  // defer
    
    go func() {  // go
        ch <- 42
    }()
    
    select {  // select
    case val := <-ch:
        fmt.Println(val)
    }
    
    return  // return
}

キーワードを理解することは、Goの文法の基礎を理解することです。25個しかないので、プログラミングしながら自然に覚えられます!

おわりに 

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

よっしー
よっしー

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

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

コメント

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