Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 16219] ファイルの概要

このコミットは、Go言語の公式ドキュメントの一部である doc/effective_go.html ファイルに対する修正です。effective_go.html は、Go言語を効果的に記述するためのプラクティスやイディオムを解説した「Effective Go」ドキュメントのHTML版であり、Goプログラミングにおけるベストプラクティスや設計原則を学ぶ上で非常に重要なリソースです。このファイルには、Go言語の機能や構文を説明するためのコード例が含まれています。

コミット

  • コミットハッシュ: ed19ab47c358c53d62bf108dfa0955367d40535c
  • Author: Shenghou Ma minux.ma@gmail.com
  • Date: Wed Apr 24 03:51:31 2013 +0800

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/ed19ab47c358c53d62bf108dfa0955367d40535c

元コミット内容

doc/effective_go.html: fix syntax error in code example
Fixes #5333.

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/8698046

変更の背景

このコミットの背景には、doc/effective_go.html 内のコード例に構文エラーが存在していたという問題があります。ドキュメント内のコード例は、読者がGo言語の特定の機能やパターンを理解するための重要な手助けとなりますが、そのコード自体に誤りがあると、読者の学習を妨げ、混乱を招く可能性があります。

コミットメッセージにある Fixes #5333 は、この変更が特定のイシュー(問題報告)を解決したことを示しています。Goプロジェクトでは、GitHubのイシュートラッカーやGoのバグトラッカーを通じて、ドキュメントの誤りやコードのバグが報告されます。この場合、おそらく誰かが effective_go.html のコード例に誤りがあることを発見し、それを報告したため、この修正が行われたと考えられます。ドキュメントの正確性を保つことは、言語の普及と利用者の学習体験にとって非常に重要です。

前提知識の解説

この変更を理解するためには、Go言語における以下の概念を理解しておく必要があります。

  1. 関数リテラル (Function Literals): Go言語では、関数をその場で定義し、変数に代入したり、他の関数の引数として渡したりすることができます。これを関数リテラルと呼びます。無名関数とも呼ばれます。

    func(a, b int) int {
        return a + b
    }
    
  2. ゴルーチン (Goroutines): Go言語のゴルーチンは、軽量な並行処理の単位です。go キーワードを関数の呼び出しの前に置くことで、その関数を新しいゴルーチンとして実行し、並行処理を実現します。

    go func() {
        // このコードは新しいゴルーチンで実行される
    }() // ここで関数リテラルが呼び出される
    
  3. 即時実行関数 (Immediately Invoked Function Expression - IIFE): JavaScriptなどの言語でよく見られるパターンですが、Goでも関数リテラルを定義した直後にその関数を呼び出すことができます。これは、関数リテラルの定義の直後に () を追加することで実現されます。この () は、関数を呼び出すためのオペレータです。

    func() {
        fmt.Println("Hello from IIFE")
    }() // この括弧が関数を即座に実行する
    

    このコミットの核心は、この「即時実行」のための () が欠落していた点にあります。go func() { ... } だけでは、関数リテラルを定義しただけで、それをゴルーチンとして「実行」する指示が不足しています。ゴルーチンとして実行するためには、go キーワードの後に「関数呼び出し」が続く必要があります。関数リテラルを呼び出すには、その定義の直後に () を付ける必要があるのです。

技術的詳細

修正されたコード例は、おそらく並行処理のセクションにあり、リクエストを処理するためのワーカープールのようなパターンを示していたと考えられます。元のコードは以下のようになっていました。

func Serve(queue chan *Request) {
    for req := range queue {
        sem <- 1
        go func() { // ここで関数リテラルが定義されている
            process(req)
            sem <- 1
        } // ここに関数呼び出しの `()` が欠落していた
    }
}</pre>

このコードでは、go func() { ... } の部分で無名関数(関数リテラル)が定義されていますが、その直後に () がありませんでした。Go言語において、関数リテラルを定義するだけでは、その関数は実行されません。関数を実行するためには、関数名の後に () を付けて呼び出す必要があります。

したがって、go func() { ... } は「新しいゴルーチンで実行されるべき関数リテラルを定義する」という意図は伝わりますが、実際にその関数リテラルを「呼び出す」というアクションが欠けていたため、コンパイルエラー(または実行時エラー、あるいは意図しない動作)を引き起こす可能性がありました。

Goコンパイラは、go キーワードの後に続くものが「関数呼び出し」であることを期待します。func() { ... } は関数リテラルそのものであり、呼び出しではありません。呼び出しにするためには、func() { ... }() のように、定義の直後に呼び出し演算子 () を付加する必要があります。

この修正は、Go言語の基本的な構文規則、特にゴルーチンと関数リテラルの組み合わせ方に関する理解の誤りを正すものです。ドキュメントのコード例は、読者がそのままコピー&ペーストして試せるように、完全に正しく動作するものであるべきです。

コアとなるコードの変更箇所

--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -2947,7 +2947,7 @@ func Serve(queue chan *Request) {
         go func() {
             process(req)
             sem &lt;- 1
-        }
+        }()
     }\
 }</pre>

コアとなるコードの解説

このdiffは、doc/effective_go.html ファイル内の特定のコードブロックにおける1行の変更を示しています。

  • - }: 変更前の行です。ここでは、無名関数(関数リテラル)の定義が閉じられています。この行の直後には、関数を呼び出すための () がありませんでした。
  • + }(): 変更後の行です。無名関数の定義を閉じる } の直後に () が追加されています。

この () の追加が、このコミットの最も重要な変更点です。これにより、定義された無名関数が go キーワードによって新しいゴルーチンとして「即座に実行」されるようになります。

具体的には、go func() { ... }() という構文は以下のように解釈されます。

  1. func() { ... }: これは引数を取らず、何も返さない無名関数(関数リテラル)の定義です。
  2. (): この無名関数を呼び出すためのオペレータです。これにより、関数リテラルが評価され、実行可能な関数オブジェクトが生成され、直ちに呼び出されます。
  3. go: この関数呼び出しを新しいゴルーチンとして実行するようにGoランタイムに指示します。

この修正により、effective_go.html に記載されているコード例がGo言語の正しい構文に準拠し、意図した通りに並行処理が実行されるようになりました。これは、ドキュメントの正確性を保ち、読者がGo言語の正しいイディオムを学ぶ上で非常に重要な修正です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語の構文に関する一般的な知識