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

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

このコミットは、Go言語の公式FAQドキュメントである doc/go_faq.html を更新するものです。Go 1のリリースに伴い、言語の仕様変更、推奨されるプラクティス、および一般的な誤解に関する情報が修正・追記されています。このファイルは、Go言語のユーザーが抱くであろう疑問に対する公式な回答を提供し、言語の設計思想や機能について理解を深めるための重要なリソースです。

コミット

このコミットは、Go言語のFAQドキュメントをGo 1のリリースに合わせて更新し、Go 1で導入された変更点や明確化された概念を反映しています。具体的には、godoc サーバーのデプロイ環境、メソッドの定義範囲、マップのキーとしての型の扱い、GOMAXPROCS の説明、クロージャとゴルーチンの挙動、パフォーマンスベンチマークに関する記述、変数宣言の構文、ガベージコレクションの実装に関する情報が修正されています。

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

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

元コミット内容

commit fcfed1479e0fd96cc8c5fb21b4b8b590267491f1
Author: Rob Pike <r@golang.org>
Date:   Mon Jan 23 08:39:53 2012 -0800

    FAQ: update to Go 1.
    
    R=adg, rsc, r
    CC=golang-dev
    https://golang.org/cl/5562051

変更の背景

この変更の主な背景は、Go言語のバージョン1(Go 1)のリリースです。Go 1は、Go言語の安定版として位置づけられ、将来の互換性を保証する最初のメジャーリリースでした。このリリースにより、言語仕様、標準ライブラリ、およびツールチェインが安定し、後方互換性が維持されることになりました。

FAQドキュメントは、Go言語に関する一般的な疑問に答えるためのものであり、Go 1のリリースに伴い、以前のバージョンからの変更点や、Go 1で明確化された概念についてユーザーに正確な情報を提供する必要がありました。特に、Go 1で導入されたマップのキーとしての構造体や配列の利用、メソッドの定義範囲の拡大、GOMAXPROCS の挙動の統一、クロージャとゴルーチンの一般的な落とし穴に関する説明の改善などが求められました。

このコミットは、Go 1のリリースに合わせてFAQを最新の状態に保ち、ユーザーがGo言語をより正確に理解し、効果的に利用できるようにするための重要なメンテナンス作業の一環です。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語に関する前提知識が必要です。

  • Go言語のFAQ (Frequently Asked Questions): Go言語の公式ウェブサイトに掲載されている、Go言語に関するよくある質問とその回答をまとめたドキュメント。言語の設計思想、機能、一般的な問題解決策などが含まれます。
  • Go 1: 2012年3月にリリースされたGo言語の最初の安定版メジャーリリース。このバージョン以降、Go言語は後方互換性を維持することを約束し、言語仕様が安定しました。
  • godoc: Go言語のドキュメント生成ツールであり、Goのソースコードからドキュメントを抽出し、ウェブサーバーとして提供する機能も持ちます。golang.org のウェブサイト自体も godoc によって提供されていました。
  • GOMAXPROCS: Goランタイムが同時に実行できるOSスレッドの最大数を制御する環境変数。Go 1以前は、gc コンパイラと gccgo で挙動が異なる場合がありましたが、Go 1で統一されました。Goプログラムが複数のCPUコアを最大限に活用するために重要です。
  • ゴルーチン (Goroutines): Go言語の軽量な並行処理単位。OSスレッドよりもはるかに軽量で、数千、数万のゴルーチンを同時に実行することが可能です。
  • チャネル (Channels): ゴルーチン間で値を安全に送受信するための通信メカニズム。CSP (Communicating Sequential Processes) の考え方に基づいています。
  • インターフェース (Interfaces): Go言語におけるポリモーフィズムを実現するための仕組み。メソッドのシグネチャの集合を定義し、任意の型がそのインターフェースのメソッドを実装することで、そのインターフェースを満たすとみなされます。
  • メソッド (Methods): 特定の型に関連付けられた関数。Goでは、構造体だけでなく、任意の型(組み込み型を含む)にメソッドを定義できます。
  • マップ (Maps): キーと値のペアを格納するGoの組み込みデータ構造。キーは比較可能でなければなりません。Go 1で、構造体や配列がマップのキーとして使用可能になりましたが、スライスは引き続き使用できません。
  • ガベージコレクション (Garbage Collection, GC): プログラムが不要になったメモリを自動的に解放する仕組み。Go言語には組み込みのGCがあります。
  • スタックとヒープ (Stack vs. Heap): プログラムがメモリを使用する2つの主要な領域。スタックは関数のローカル変数などに使われ、ヒープは動的に割り当てられるメモリに使われます。Goコンパイラは、変数がスタックに割り当てられるかヒープに割り当てられるかを自動的に決定します(エスケープ解析)。
  • クロージャ (Closures): 周囲の環境(スコープ)の変数を参照できる関数。並行処理と組み合わせる際に、変数のキャプチャに関する注意点があります。

技術的詳細

このコミットにおける技術的な変更点は多岐にわたりますが、Go 1のリリースに伴う言語仕様の安定化と明確化を反映しています。

  1. golang.org のサーバー環境の明確化:

    • 以前は単に godoc サーバーがプロダクション環境で動作していると記述されていましたが、Go 1のリリースに合わせて「Google App Engine上で動作している」という情報が追加されました。これは、GoがGoogleのインフラストラクチャで実際に利用されていることを示す具体的な例として強調されています。
  2. メソッドの定義範囲の拡大:

    • Go 1では、メソッドが構造体(クラス)だけでなく、組み込み型(例:int などの「アンボックス化された」整数型)にも定義できることが明確にされました。これは、GoのメソッドがC++やJavaよりも汎用的であるという設計思想を強調するものです。
  3. 動的ディスパッチと静的解決:

    • メソッドの動的ディスパッチ(実行時に呼び出すメソッドが決定されること)は、Goではインターフェースを介してのみ行われることが再確認されました。構造体やその他の具象型に定義されたメソッドは常に静的に解決される(コンパイル時に呼び出すメソッドが決定される)という点が強調されています。
  4. インターフェース実装の検証構文:

    • var _ I = T{} という構文が、型 T がインターフェース I を実装していることをコンパイル時に検証するための慣用的な方法として明記されました。これは、Goのインターフェースが暗黙的に実装される(implements キーワードがない)ため、開発者が意図した通りに実装されているかを確認するのに役立ちます。
  5. マップのキーとしての型の扱い:

    • Go 1の最も重要な変更点の一つとして、構造体と配列がマップのキーとして使用可能になったことがFAQに追記されました。これは、Go 1以前は構造体と配列に等価性演算子(==)が定義されていなかったため、マップのキーとして使用できませんでしたが、Go 1で等価性が定義されたためです。
    • しかし、スライスは引き続きマップのキーとして使用できないことが明確にされています。これは、スライスの等価性の定義が複雑である(シャロー比較 vs. ディープ比較、ポインタ vs. 値比較、再帰的な構造の扱いなど)ため、Go 1時点では明確な定義が避けられたためです。
  6. スタックとヒープの割り当てに関する補足:

    • コンパイラが変数が関数から戻った後も参照されないことを証明できない場合、その変数はガベージコレクトされるヒープに割り当てられるという既存の説明に加え、「ローカル変数が非常に大きい場合、スタックではなくヒープに格納する方が理にかなっている可能性がある」という補足が追加されました。これは、エスケープ解析の挙動とパフォーマンスへの影響に関する理解を深めるものです。
  7. GOMAXPROCS の説明の簡素化と統一:

    • Go 1以前の gc コンパイラと gccgo コンパイラ間での GOMAXPROCS の挙動の違いに関する記述が削除され、Go 1で統一された挙動(GOMAXPROCS を設定することでランタイムが複数のOSスレッドを利用できるようになる)のみが残されました。これにより、ユーザーはGo 1以降の標準的な挙動を理解しやすくなりました。
    • また、「GOMAXPROCS > 1 がプログラムを遅くすることがある」という項目から、「これは gc コンパイラに特有のものである」という注釈が削除されました。これは、Go 1でランタイムのスケジューラが改善され、この問題がより一般的な並行処理のオーバーヘッドの問題として扱われるようになったことを示唆しています。
  8. クロージャとゴルーチンの挙動の明確化:

    • for ループ内でゴルーチンを起動し、ループ変数をクロージャ内で使用する際の一般的な落とし穴(c, c, c と出力される問題)について、より詳細な説明が追加されました。各クロージャが同じループ変数のインスタンスを共有するため、ゴルーチンが実行される時点での変数の最終値が参照されてしまうことが強調されています。
    • この問題の解決策として、ループ変数をゴルーチンに引数として渡すことで、各ゴルーチンが変数のコピーを受け取るようにする慣用的な方法が示されています。
  9. パフォーマンスベンチマークの参照更新:

    • Goのパフォーマンスベンチマークに関する説明において、具体的なベンチマークファイルへのリンク(例:pidigits.go, regex-dna.go, reverse-complement.go)が追加されました。これにより、ユーザーは言及されているベンチマークのソースコードを直接確認できるようになりました。
    • 特に pidigits ベンチマークでCバージョンがGMP(GNU Multiple Precision Arithmetic Library)を使用していること、regex-dna ベンチマークでGoの regexp パッケージがPCREのような成熟したライブラリと比較されていることが明記され、ベンチマーク結果の背景がより明確になりました。
  10. ガベージコレクションの実装に関する更新:

    • Goのガベージコレクタについて、「現在の実装はプレーンなマーク&スイープコレクタだが、代替案が検討中である」という記述が削除され、「現在の実装は並行マーク&スイープコレクタだが、将来のバージョンでは異なるアプローチを取る可能性がある」という表現に更新されました。これは、Go 1時点でのGCの実装が並行処理に対応していることを示しつつ、将来的な改善の可能性を示唆しています。

これらの変更は、Go 1のリリースによってGo言語がより成熟し、その機能と挙動がより明確に定義されたことを反映しています。FAQの更新は、ユーザーがこれらの変更を理解し、Go言語を効果的に使用するための重要なガイドとなります。

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

このコミットは doc/go_faq.html ファイルのみを変更しています。以下に主要な変更箇所を抜粋し、その意味を説明します。

  1. golang.org サーバーの記述変更:

    --- a/doc/go_faq.html
    +++ b/doc/go_faq.html
    @@ -190,10 +190,11 @@ easier to understand what happens when things combine.
     
     <p>
     Yes. There are now several Go programs deployed in 
    -production inside Google.  For instance, the server behind
    -<a href="http://golang.org">http://golang.org</a> is a Go program; 
    -in fact it's just the <a href="/cmd/godoc"><code>godoc</code></a>
    -document server running in a production configuration.
    +production inside Google.  A public example is the server behind
    +<a href="http://golang.org">http://golang.org</a>.
    +It's just the <a href="/cmd/godoc"><code>godoc</code></a>
    +document server running in a production configuration on
    +<a href="http://code.google.com/appengine/">Google App Engine</a>.
     </p>
    
    • golang.org のサーバーがGoogle App Engine上で動作していることを明記。
  2. メソッドの定義範囲の拡大:

    --- a/doc/go_faq.html
    +++ b/doc/go_faq.html
    @@ -433,7 +434,9 @@ we believe is easy to use and in some ways more general. There are
     also ways to embed types in other types to provide something
     analogous&mdash;but not identical&mdash;to subclassing.
     Moreover, methods in Go are more general than in C++ or Java:
    -they can be defined for any sort of data, not just structs.
    +they can be defined for any sort of data, even built-in types such
    +as plain, &ldquo;unboxed&rdquo; integers.
    +They are not restricted to structs (classes).
     </p>
    
    • メソッドが構造体だけでなく、組み込み型にも定義できることを強調。
  3. マップのキーとしての型の扱い:

    --- a/doc/go_faq.html
    +++ b/doc/go_faq.html
    @@ -750,17 +753,22 @@ to write one but it will not be as convenient syntactically; this seems a reason
     </p>
     
     <h3 id="map_keys">
    -Why don't maps allow structs and arrays as keys?</h3>
    +Why don't maps allow slices as keys?</h3>
     <p>
    -Map lookup requires an equality operator, which structs and arrays do not implement.
    +Map lookup requires an equality operator, which slices do not implement.
     They don't implement equality because equality is not well defined on such types;
     there are multiple considerations involving shallow vs. deep comparison, pointer vs.
    -value comparison, how to deal with recursive structures, and so on.
    -We may revisit this issue&mdash;and implementing equality for structs and arrays
    +value comparison, how to deal with recursive types, and so on.
    +We may revisit this issue&mdash;and implementing equality for slices
     will not invalidate any existing programs&mdash;but without a clear idea of what
     equality of structs and arrays should mean, it was simpler to leave it out for now.
     </p>
     
    +<p>
    +In Go 1, equality is defined for structs and arrays, so such
    +types can be used as map keys, but slices still do not have a definition of equality.
    +</p>
    +
     <h3 id="references">
     Why are maps, slices, and channels references while arrays are values?</h3>
    
    • Go 1で構造体と配列がマップのキーとして使用可能になったことを追記。スライスは引き続き不可。
  4. GOMAXPROCS の説明の簡素化:

    --- a/doc/go_faq.html
    +++ b/doc/go_faq.html
    @@ -993,10 +1003,8 @@ See the <a href="/doc/codewalk/sharemem/">Share Memory By Communicating</a> code
     Why doesn't my multi-goroutine program use multiple CPUs?</h3>
     
     <p>
    -Under the gc compilers you must set <code>GOMAXPROCS</code> to allow the
    -run-time support to utilise more than one OS thread. Under <code>gccgo</code> an OS
    -thread will be created for each goroutine, and <code>GOMAXPROCS</code> is
    -effectively equal to the number of running goroutines.  
    +You must set <code>GOMAXPROCS</code> to allow the
    +run-time support to utilize more than one OS thread. 
     </p>
    
    • gccgo に特化した記述を削除し、Go 1での統一された挙動を説明。
  5. クロージャとゴルーチンの挙動の明確化:

    --- a/doc/go_faq.html
    +++ b/doc/go_faq.html
    @@ -1076,29 +1080,30 @@ Consider the following program:
      
      <pre>
      func main() {
    -	done := make(chan bool)
    -
    -	values := []string{ "a", "b", "c" }
    -	for _, v := range values {
    -		go func() {
    -			fmt.Println(v)
    -			done <- true
    -		}()
    -	}
    -
    -	// wait for all goroutines to complete before exiting
    -	for _ = range values {
    -		<-done 
    -	}
    +    done := make(chan bool)
    +
    +    values := []string{ "a", "b", "c" }
    +    for _, v := range values {
    +        go func() {
    +            fmt.Println(v)
    +            done <- true
    +        }()
    +    }
    +
    +    // wait for all goroutines to complete before exiting
    +    for _ = range values {
    +        <-done 
    +    }
      }
      </pre>
      
      <p>
      One might mistakenly expect to see <code>a, b, c</code> as the output. 
      What you'll probably see instead is <code>c, c, c</code>.  This is because 
    -each closure shares the same variable <code>v</code>. Each closure prints the 
    -value of <code>v</code> at the time <code>fmt.Println</code> is executed, 
    -rather than the value of <code>v</code> when the goroutine was launched. 
    +each iteration of the loop uses the same instance of the variable <code>v</code>, so
    +each closure shares that single variable. When the closure runs, it prints the 
    +value of <code>v</code> at the time <code>fmt.Println</code> is executed,
    +but <code>v</code> may have been modified since the goroutine was launched. 
      </p>
      
      <p>
    @@ -1107,12 +1112,12 @@ could modify the inner loop to read:
      </p>
      
      <pre>
    -	for _, v := range values {
    -		go func(<b>u</b> string) {
    -			fmt.Println(<b>u</b>)
    -			done <- true
    -		}(<b>v</b>)
    -	}
    +    for _, v := range values {
    +        go func(<b>u</b> string) {
    +            fmt.Println(<b>u</b>)
    +            done <- true
    +        }(<b>v</b>)
    +    }
      </pre>
    
    • クロージャがループ変数を共有する問題とその解決策を詳細に説明。
  6. ガベージコレクションの実装に関する更新:

    --- a/doc/go_faq.html
    +++ b/doc/go_faq.html
    @@ -1495,8 +1503,7 @@ memory management.  We feel it's critical to eliminate that
     programmer overhead, and advances in garbage collection
     technology in the last few years give us confidence that we can
     implement it with low enough overhead and no significant
    -latency.  (The current implementation is a plain mark-and-sweep
    -collector but a replacement is in the works.)
    +latency.
     </p>
      
     <p>
    @@ -1515,6 +1522,11 @@ Finally, concurrency aside, garbage collection makes interfaces
     simpler because they don't need to specify how memory is managed across them.
     </p>
      
    +<p>
    +The current implementation is a parallel mark-and-sweep
    +collector but a future version might take a different approach.
    +</p>
    +
     <p>
     On the topic of performance, keep in mind that Go gives the programmer
     considerable control over memory layout and allocation, much more than
    
    • GCが並行マーク&スイープコレクタであること、将来的な変更の可能性を記述。

コアとなるコードの解説

これらの変更は、Go 1のリリースがGo言語にとって重要なマイルストーンであったことを反映しています。

  • golang.org のApp Engineへの言及: これは、GoがGoogle内部で実際に使用され、その安定性とスケーラビリティが実証されていることを示す具体的な証拠として機能します。App EngineはGoogleのクラウドプラットフォームであり、Goがその上で動作することは、Goがウェブサービスやクラウドネイティブアプリケーションに適していることを示唆しています。
  • メソッドの定義範囲の拡大: Goのメソッドが構造体だけでなく、任意の型に定義できるという事実は、Goの設計がオブジェクト指向プログラミングの伝統的なクラスベースのアプローチとは異なる、より柔軟なアプローチを取っていることを示しています。これにより、既存の型に新しい振る舞いを簡単に追加できるようになり、コードの再利用性と表現力が高まります。
  • マップのキーとしての構造体と配列の許可: これはGo 1における重要な言語仕様の変更点です。構造体や配列がマップのキーとして使えるようになったことで、より複雑なデータ構造を直接マップのキーとして利用できるようになり、開発の柔軟性が向上しました。ただし、スライスが引き続きキーとして使えないのは、その等価性の定義が曖牲であるためであり、Go言語の設計者が明確なセマンティクスを重視していることを示しています。
  • GOMAXPROCS の説明の統一: Go 1でランタイムのスケジューラが改善され、GOMAXPROCS の挙動がより予測可能かつ統一的になったことを反映しています。これにより、ユーザーはGoプログラムの並行処理性能をより簡単に制御できるようになりました。
  • クロージャとゴルーチンの挙動の明確化: for ループ内でゴルーチンとクロージャを組み合わせる際の一般的な落とし穴は、Goの並行処理を学ぶ上で多くの開発者が直面する問題でした。FAQでこの問題とその解決策が明確に示されたことで、ユーザーはより安全で意図通りの並行プログラムを書くことができるようになります。これは、Go言語が並行処理を重視しているからこそ、その落とし穴についても丁寧に説明する必要があるという姿勢を示しています。
  • ガベージコレクションの説明の更新: GCの実装が「並行マーク&スイープ」であると明記されたことは、GoのGCがプログラムの実行と並行して動作し、アプリケーションの停止時間を最小限に抑えるように設計されていることを示しています。これは、低レイテンシが求められるサーバーアプリケーションにとって重要な特性です。将来的なアプローチの可能性に言及することで、GoのGCが継続的に改善されていくことが示唆されています。

これらの変更は、Go 1が単なるバージョンアップではなく、Go言語の成熟と安定化に向けた大きな一歩であったことを示しています。FAQの更新は、これらの重要な変更点をユーザーに伝え、Go言語の理解を深める上で不可欠な役割を果たしました。

関連リンク

参考にした情報源リンク