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

スポンサーリンク
Go言語入門:言語仕様 -Vol.121- 用語解説
Go言語入門:言語仕様 -Vol.121-
この記事は約5分で読めます。
よっしー
よっしー

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

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

スポンサーリンク

背景

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

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

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

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

goto文(Goto statements)

goto 文は、同じ関数内の対応するラベルを持つ文に制御を移す。

GotoStmt = "goto" Label .
goto Error

goto 文の実行によって、goto の地点でまだスコープに入っていなかった変数がスコープに入るようになってはならない。たとえば、以下の例は:

	goto L  // 不正
	v := 3
L:

ラベル L へのジャンプが v の作成をスキップするため、誤りである。

ブロックの外にある goto 文は、そのブロックの内部のラベルにジャンプすることはできない。たとえば、以下の例は:

if n%2 == 1 {
	goto L1
}
for n > 0 {
	f()
	n--
L1:
	f()
	n--
}

ラベル L1for 文のブロックの内部にあるが、goto はそうではないため、誤りである。


解説

goto文の基本

goto は、指定したラベルの位置に直接ジャンプする文です。

func example() {
    fmt.Println("1")
    goto skip
    fmt.Println("2")  // スキップされる
skip:
    fmt.Println("3")
}
// 出力: 1, 3

2つの制限

Go の goto には、安全性のために2つの厳しい制限が設けられています。

制限1:変数宣言を飛び越えてはいけない

goto L    // コンパイルエラー!
v := 3    // この宣言がスキップされてしまう
L:
fmt.Println(v)  // v が作られていないのに使おうとしている

もしこのジャンプが許されたら、初期化されていない変数 v を使ってしまう可能性があります。Go はこれをコンパイル時に禁止しています。

回避するには、変数の宣言を goto の前に移動します。

v := 0    // 先に宣言しておく
goto L
v = 3     // 代入はスキップされるが、v 自体は存在する
L:
fmt.Println(v)  // v=0(スキップされたので代入は行われない)

制限2:ブロックの外から中にジャンプできない

if n%2 == 1 {
    goto L1       // コンパイルエラー!
}
for n > 0 {
    f()
    n--
L1:               // for ブロックの中にある
    f()
    n--
}

for ブロックの外にいる goto が、ブロックの途中にジャンプしようとしています。これが許されると、ループの初期化処理をスキップして不正な状態に入る可能性があります。

逆方向(ブロックの中から外へ)は許可されています。

for n > 0 {
    if condition {
        goto done  // ブロックの中から外へのジャンプは OK
    }
    n--
}
done:
fmt.Println("完了")

goto はほとんど使わない

Go に goto がある理由は、ごくまれにコードが簡潔になる場面があるからです。しかし、ほぼすべてのケースで breakcontinuereturndefer で代替できます

// goto を使うケース(非推奨)
func process() error {
    resource := acquire()
    if err := step1(); err != nil {
        goto cleanup
    }
    if err := step2(); err != nil {
        goto cleanup
    }
    release(resource)
    return nil
cleanup:
    release(resource)
    return err
}

// defer を使うケース(推奨)
func process() error {
    resource := acquire()
    defer release(resource)
    if err := step1(); err != nil {
        return err
    }
    return step2()
}

Go の標準ライブラリでも goto が使われている箇所はごくわずかで、主にパーサーや状態機械の実装など、特殊な場面に限られます。普段の開発で使うことはまずないと考えて大丈夫です。

おわりに 

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

よっしー
よっしー

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

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

コメント

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