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

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

このコミットは、Go言語の公式ドキュメントである doc/effective_go.html に、Goの「ブランク識別子 (blank identifier)」に関する新しいセクションを追加するものです。ブランク識別子 _ は、Go言語において特定の値を破棄したり、変数の未使用エラーを抑制したり、パッケージの副作用のみをインポートしたりする際に使用される特殊な識別子です。このコミットにより、effective_go.html の内容は150行追加され、doc/progs/unused1.godoc/progs/unused2.go という2つの新しいサンプルコードファイルが追加されています。

コミット

commit f8284b64cecf38338cf62ddc6398c0a9fe655326
Author: Russ Cox <rsc@golang.org>
Date:   Tue Jan 22 14:00:10 2013 -0500

    doc/effective_go.html: add a section about the blank identifier
    
    R=golang-dev, minux.ma, bradfitz, adg
    CC=golang-dev
    https://golang.org/cl/7134056

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

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

元コミット内容

doc/effective_go.html: add a section about the blank identifier

このコミットは、doc/effective_go.html にブランク識別子に関するセクションを追加します。

変更の背景

Go言語には、他の多くのプログラミング言語には見られないユニークな機能として「ブランク識別子 (_)」が存在します。これは、多値返却、未使用変数・インポートのコンパイルエラー回避、パッケージの副作用のみのインポート、インターフェースの実装チェックなど、様々な場面で活用されます。

Effective Go は、Go言語を効果的に書くための慣用的なスタイルやパターンを解説する公式ドキュメントであり、Goプログラマーにとって非常に重要なリソースです。しかし、このコミット以前は、ブランク識別子に関する包括的な解説が Effective Go には含まれていませんでした。

この変更の背景には、ブランク識別子の重要性と、その適切な使用方法をGoプログラマーに広く理解してもらう必要があったと考えられます。特に、Goの厳格な未使用変数・インポートチェックは、他の言語からの移行者にとって戸惑いの原因となることがあり、ブランク識別子はその問題を解決する鍵となります。また、インターフェースの実装チェックなど、より高度なGoのイディオムを理解するためにも、ブランク識別子の知識は不可欠です。

このコミットは、Go言語の設計思想と慣用的なプログラミングスタイルをより明確に伝えるために、Effective Go の内容を充実させる一環として行われました。

前提知識の解説

このコミットの技術的詳細を理解するためには、以下のGo言語の概念に関する前提知識が必要です。

  1. Go言語の基本的な構文とセマンティクス: 変数宣言、関数定義、for ループ、if 文など、Go言語の基本的な文法を理解している必要があります。
  2. 多値返却 (Multiple Return Values): Goの関数は複数の値を返すことができます。例えば、value, err := someFunction() のように、結果とエラーを同時に返すパターンはGoで非常に一般的です。
  3. for...range ループ: Goの for...range ループは、配列、スライス、マップ、文字列、チャネルなどのコレクションをイテレートするために使用されます。通常、インデックスと値(またはキーと値)のペアを返します。
  4. 未使用変数・インポートのコンパイルエラー: Goコンパイラは、宣言されたが使用されていない変数やインポートされたが使用されていないパッケージに対して、厳格にコンパイルエラーを発生させます。これは、コードの肥大化を防ぎ、潜在的なバグを早期に発見するためのGoの設計思想の一部です。
  5. パッケージの初期化関数 (init function): Goのパッケージは、プログラムの実行開始前に自動的に呼び出される init 関数を持つことができます。これは、パッケージ固有の初期化処理(例:データベース接続の確立、HTTPハンドラの登録など)を行うために使用されます。
  6. インターフェース (Interfaces): Goのインターフェースは、メソッドのシグネチャの集合を定義します。Goの型は、明示的にインターフェースを実装すると宣言する必要はなく、インターフェースで定義されたすべてのメソッドを実装していれば、そのインターフェースを「暗黙的に」実装していると見なされます。これは「ダックタイピング」とも呼ばれます。
  7. 型アサーション (Type Assertion): インターフェース型の値が、特定の具象型または別のインターフェース型であるかどうかをチェックし、その型に変換するために使用されます。例: value, ok := interfaceVar.(ConcreteType)
  8. HTMLの基本的な構造: doc/effective_go.html はHTMLファイルであるため、h2, h3, p, pre, a, code などのHTMLタグの基本的な意味を理解していると、変更内容がより分かりやすくなります。

これらの概念を理解することで、ブランク識別子がGo言語の様々な側面とどのように連携し、コードの可読性、堅牢性、および慣用的なスタイルにどのように貢献するかが明確になります。

技術的詳細

このコミットは、doc/effective_go.html に「Blank identifier」という新しいセクションを追加し、Go言語の特殊な識別子である _ (アンダースコア) の多岐にわたる用途を詳細に解説しています。

追加されたセクションは、以下のサブセクションで構成されています。

  1. Multiple assignment (多値代入):

    • Goの関数が複数の値を返す場合や、for...range ループでインデックスと値の両方を返す場合に、不要な値を破棄するためにブランク識別子を使用する方法を説明しています。
    • 例として、for _, value := range array のようにインデックスを無視するケースや、if _, err := os.Stat(path); os.IsNotExist(err) のように関数の戻り値のうちエラーのみに関心があるケースが挙げられています。
    • また、fi, _ := os.Stat(path) のようにエラーを意図的に破棄する危険性についても警告しています。これは、関数が失敗した場合にプログラムがパニックする可能性があるため、通常は避けるべき慣用句とされています。
  2. Unused imports and variables (未使用のインポートと変数):

    • Goコンパイラが未使用のインポートや変数に対してエラーを出すというGoの厳格なルールについて説明しています。これは、プログラムの肥大化を防ぎ、潜在的なバグを早期に発見するためです。
    • 開発中のコードで一時的に未使用のインポートや変数が発生する場合に、ブランク識別子を使ってこれらのエラーを抑制する方法が示されています。
    • var _ = fmt.Printfvar _ io.Reader のように、パッケージのトップレベルでブランク識別子に割り当てることで、未使用インポートのエラーを抑制できることを解説しています。
    • 同様に、_ = greeting のように変数をブランク識別子に割り当てることで、未使用変数のエラーを抑制できることを示しています。
    • このセクションでは、doc/progs/unused1.go (未使用のインポートと変数があるコード) と doc/progs/unused2.go (ブランク識別子でエラーを抑制したコード) の2つの新しいサンプルコードが参照されています。
  3. Import for side effect (副作用のためのインポート):

    • 特定のパッケージが、その init 関数を通じてのみ利用され、明示的な関数呼び出しや変数参照がない場合に、ブランク識別子を使ってインポートする方法を説明しています。
    • 例として、net/http/pprof パッケージがHTTPハンドラを登録するためにインポートされるケースが挙げられています。
    • import _ "net/http/pprof" の形式でインポートすることで、そのパッケージが副作用のためにのみインポートされていることを明確に示し、未使用インポートのエラーを回避できることを解説しています。
  4. Interface checks (インターフェースのチェック):

    • Goのインターフェースが暗黙的に実装される(型がインターフェースのメソッドをすべて実装していれば、そのインターフェースを実装していると見なされる)という特徴を再確認しています。
    • コンパイル時に静的にチェックされるインターフェース変換の例(例:*os.Fileio.Reader に渡す)を挙げつつ、encoding/json パッケージの json.Marshaler のように、実行時にのみチェックされるインターフェースの実装があることを説明しています。
    • var _ json.Marshaler = (*MyMessage)(nil) のように、ブランク識別子を使った宣言を利用することで、コンパイル時に特定の型がインターフェースを実装しているかを静的にチェックできるテクニックを解説しています。これは、型チェックのためだけに存在し、変数を作成しないことを示しています。

これらの詳細な解説により、Goプログラマーはブランク識別子の多様な用途と、それがGoのコードベースでどのように慣用的に使用されるかを深く理解できるようになります。

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

このコミットの主要な変更は、doc/effective_go.html ファイルへのHTMLコンテンツの追加です。

--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -2966,6 +2966,156 @@ filter unexpected problems and re-panic with the original error.
 That's left as an exercise for the reader.
 </p>
 
+<h2 id="blank">Blank identifier</h2>
+
+<p>
+Go defines a special identifier <code>_</code>, called the <i>blank identifier</i>.
+The blank identifier can be used in a declaration to avoid
+declaring a name, and it can be used in an assignment to discard a value.
+This definition makes it useful in a variety of contexts.
+</p>
+
+<h3 id="blank_assign">Multiple assignment</h3>
+
+<p>
+If an assignment requires multiple values on the left side,
+but one of the values will not be used by the program,
+using the blank identifier in the assignment avoids the need
+to create a dummy variable.
+We saw one example of this in the discussion of
+<a href="#for">for loops</a> above.
+</p>
+<pre>
+sum := 0
+for _, value := range array {
+    sum += value
+}
+</pre>
+
+<p>
+Another common use is when calling a function that returns
+a value and an error, but only the error is important.
+</p>
+<pre>
+if _, err := os.Stat(path); os.IsNotExist(err) {
+	fmt.Printf("%s does not exist\n", path)
+}
+</pre>
+
+<p>
+A final use that is more common than it should be is to 
+discard the error from a function that is not expected to fail.
+This is usually a mistake: when the function does fail, the code
+will continue on and probably panic dereferencing a nil pointer.
+</p>
+<pre>
+// Always check errors: this program crashes if path does not exist.
+fi, _ := os.Stat(path)
+fmt.Printf("%s is %d bytes\n", path, fi.Size())\n</pre>
+
+<h3 id="blank_unused">Unused imports and variables</h3>
+
+<p>
+Go defines that it is an error to import a package without using it,
+or to declare a variable without using its value.
+Unused imports bloat a program and lengthen compiles unnecessarily;
+a variable that is initialized but not used is at least
+a wasted computation and perhaps indicative of a
+larger bug.
+Of course, both of these situations also arise in programs
+that are under active development, as you test and refine
+your code. 
+</p>
+<p>
+For example, in this program, there are two unused imports
+(<code>fmt</code> and <code>io</code>)
+and an unused variable (<code>greeting</code>).
+</p>
+{{code "/doc/progs/unused1.go" `/package/` `$`}}
+<p>
+Top-level blank declarations referring to the packages
+will silence the unused import errors.
+By convention, these declarations should come immediately after
+the imports, as a reminder to clean things up later.
+Similarly, assigning <code>greeting</code> to a blank identifier
+will silence the unused variable error.
+</p>
+{{code "/doc/progs/unused2.go" `/package/` `$`}}
+<p>
+
+<h3 id="blank_import">Import for side effect</h3>
+
+<p>
+An unused import like <code>fmt</code> or <code>io</code> in the last section
+should eventually be used or removed:
+blank assignments identify code as a work in progress.
+But sometimes it is useful to import a package only for its
+side effects, without any explicit use.\n+For example, during its <code>init</code> function,
+the <code><a href="/pkg/net/http/pprof/">net/http/pprof</a></code>
+package registers HTTP handlers that provide useful
+debugging information. It has an exported API too, but
+most clients need only the handler registration.
+In this situation, it is conventional to rename the package
+to the blank identifier:
+</p>
+<pre>
+import _ "net/http/pprof"
+</pre>
+<p>
+This form of import makes clear that the package is being
+imported for its side effects, because there is no other possible
+use of the package: in this file, it doesn't have a name.
+</p>
+
+<h3 id="blank_implements">Interface checks</h3>
+
+<p>
+As we saw in the discussion of <a href="#interfaces_and_types">interfaces</a> above,
+Go does not require a type to declare explicitly that it implements an interface.
+It implements the interface by simply implementing the required methods.
+This makes Go programs more lightweight and flexible, and it can avoid
+unnecessary dependencies between packages. 
+Most interface conversions are static, visible to the compiler,
+and therefore checked at compile time.
+For example, passing an <code>*os.File</code> to a function
+expecting an <code>io.Reader</code> will not compile unless
+<code>*os.File</code> implements the <code>io.Reader</code> interface.
+</p>
+<p>
+However, some types that are used only to satisfy dynamic interface checks.
+For example, the <code><a href="/pkg/encoding/json/">encoding/json</a></code>
+package defines a <code><a href="/pkg/encoding/json/#Marshaler">Marshaler</a></code>
+interface. If the JSON encoder encounters a type implementing that interface,
+the encoder will let the type convert itself to JSON instead of using the standard
+conversion.
+This check is done only at runtime, with code like:
+</p>
+<pre>
+m, ok := val.(json.Marshaler)
+</pre>
+<p>
+If a type—for example,
+<code><a href="/pkg/encoding/json/#RawMessage">json.RawMessage</a></code>—intends
+to customize its JSON representation, it should implement
+<code>json.Marshaler</code>, but there are no static conversions that would
+cause the compiler to verify this automatically.
+A declaration can be used to add such a check:
+</p>
+<pre>
+var _ json.Marshaler = (*MyMessage)(nil)
+</pre>
+<p>
+As part of type-checking this static assignment of a
+<code>*RawMessage</code> to a <code>Marshaler</code>,
+the Go compiler will require that <code>*RawMessage</code> implements <code>Marshaler</code>.
+Using the blank identifier here indicates that
+the declaration exists only for the type checking,
+not to create a variable.
+Conventionally, such declarations are used only when there are
+no static conversions already present in the code.
+</p>
 
 <h2 id="web_server">A web server</h2>
 

また、以下の2つの新しいGoプログラムファイルが追加されています。

  • doc/progs/unused1.go: 未使用のインポートと変数を含むGoコードの例。
  • doc/progs/unused2.go: unused1.go のコードをブランク識別子を使って修正し、未使用エラーを抑制した例。
--- /dev/null
+++ b/doc/progs/unused1.go
@@ -0,0 +1,12 @@
+// skip
+
+package main
+
+import (
+	"fmt"
+	"io"
+)
+
+func main() {
+	greeting := "hello, world"
+}
--- /dev/null
+++ b/doc/progs/unused2.go
@@ -0,0 +1,16 @@
+// compile
+
+package main
+
+import (
+	"fmt"
+	"io"
+)
+
+var _ = fmt.Printf
+var _ io.Reader
+
+func main() {
+	greeting := "hello, world"
+	_ = greeting
+}

コアとなるコードの解説

追加されたHTMLコンテンツは、Go言語のブランク識別子 _ の様々な使用例と、それがGoの慣用的なプログラミングスタイルにどのように貢献するかを詳細に説明しています。

  1. 多値代入におけるブランク識別子:

    • for _, value := range array: range ループでインデックスが不要な場合に、インデックスのプレースホルダーとして _ を使用します。これにより、未使用変数エラーを回避し、コードの意図を明確にします。
    • if _, err := os.Stat(path); os.IsNotExist(err): 関数が複数の値を返す場合(ここでは os.Stat がファイル情報とエラーを返す)、特定の値(ここではファイル情報)が不要で、エラーのみに関心がある場合に _ を使用します。
    • fi, _ := os.Stat(path): エラーを意図的に破棄する例ですが、これは通常推奨されません。エラーハンドリングを怠ると、予期せぬランタイムエラー(例:nilポインタ参照)につながる可能性があるため、注意が必要です。
  2. 未使用のインポートと変数の抑制:

    • Goは未使用のインポートや変数に対してコンパイルエラーを発生させます。これはコードの品質を保つためのGoの設計思想です。
    • var _ = fmt.Printfvar _ io.Reader: パッケージレベルで _ に値を割り当てることで、そのパッケージがインポートされているが、直接使用されていないことをコンパイラに伝えます。これは、開発中に一時的に未使用となるインポートを抑制する際に便利です。
    • _ = greeting: 関数内で宣言された変数が未使用の場合に、その変数を _ に割り当てることで、未使用変数エラーを抑制します。これも開発中のコードで一時的に使用しない変数を扱う際に役立ちます。
  3. 副作用のためのインポート:

    • import _ "net/http/pprof": パッケージがその init 関数を通じてのみ機能を提供し、エクスポートされた関数や変数を使用しない場合に、_ を使ってインポートします。これにより、そのパッケージが副作用のためにインポートされていることが明確になり、未使用インポートエラーを回避できます。
  4. インターフェースの実装チェック:

    • var _ json.Marshaler = (*MyMessage)(nil): Goのインターフェースは暗黙的に実装されますが、特定の型がインターフェースを実装していることをコンパイル時に静的に確認したい場合にこのパターンを使用します。nil を特定の型にキャストして _ に割り当てることで、コンパイラはその型がインターフェースを実装しているかをチェックします。これは、実行時エラーを防ぐための強力なテクニックです。

これらのコード例と解説は、Goプログラマーがブランク識別子を効果的に、かつ慣用的に使用するための実践的なガイドラインを提供しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント Effective Go の該当コミット内容
  • Go言語のブランク識別子に関する一般的な解説記事 (Web検索結果より)