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

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

このコミットは、Go言語の公式ドキュメント「Effective Go」内の「ブランク識別子 (blank identifier)」に関するセクションの構成と内容を大幅に改善するものです。具体的には、ブランク識別子の説明セクションをドキュメントのより適切な位置に移動させ、その用途に関する解説をより詳細かつ網羅的に再構築しています。また、関連するコード例も更新され、動作確認が可能な状態に修正されています。

コミット

commit 9e329a0d16a053a884ef02d4e17a50cafea61afc
Author: Rob Pike <r@golang.org>
Date:   Fri Mar 8 10:41:20 2013 -0800

    effective_go.html: move and rework the blank identifier section
    Also rename the relevant examples and make sure the working one compiles.
    
    R=golang-dev, bradfitz, adg, iant, rsc
    CC=golang-dev
    https://golang.org/cl/7597043
---
 doc/effective_go.html    | 351 ++++++++++++++++++++++++++---------------------
 doc/progs/eff_unused1.go |  18 +++
 doc/progs/eff_unused2.go |  22 +++
 doc/progs/run            |   1 +
 doc/progs/unused1.go     |  12 --
 doc/progs/unused2.go     |  16 ---
 6 files changed, 238 insertions(+), 182 deletions(-)

diff --git a/doc/effective_go.html b/doc/effective_go.html
index 0986e53849..570ca05234 100644
--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -695,6 +695,9 @@ for _, value := range array {\
 }\
 </pre>\
 \
+<p>\
+The blank identifier has many uses, as described in <a href="#blank">a later section</a>.\
+\
 <p>\
 For strings, the <code>range</code> does more work for you, breaking out individual\
 Unicode code points by parsing the UTF-8.\
@@ -802,6 +805,8 @@ func Compare(a, b []byte) int {\
 }\
 </pre>\
 \
+<h2 id="type_switch">Type switch</h2>\
+\
 <p>\
 A switch can also be used to discover the dynamic type of an interface\
 variable.  Such a <em>type switch</em> uses the syntax of a type\
@@ -1556,11 +1561,8 @@ func offset(tz string) int {\
 </pre>\
 <p>\
 To test for presence in the map without worrying about the actual value,\
-you can use the blank identifier (<code>_</code>).\
-The blank identifier can be assigned or declared with any value of any type, with the\n-value discarded harmlessly; it's a bit like writing to the Unix <code>/dev/null</code> file.\n-For testing just presence in a map, use the blank\n-identifier in place of the usual variable for the value.\
+you can use the <a href="#blank">blank identifier</a> (<code>_</code>)\
+in place of the usual variable for the value.\
 </p>\
 <pre>\
 _, present := timeZone[tz]\
@@ -2312,6 +2314,196 @@ a channel, and a function, all because interfaces are just sets of\
 methods, which can be defined for (almost) any type.\
 </p>\n \
+\n+<h2 id="blank">The blank identifier</h2>\
+\n+<p>\
+We've mentioned the blank identifier a couple of times now, in the context of\n+<a href="#for"><code>for</code> <code>range</code> loops</a>\n+and <a href="#maps">maps</a>.\n+The blank identifier can be assigned or declared with any value of any type, with the\n+value discarded harmlessly.\n+It's a bit like writing to the Unix <code>/dev/null</code> file:\n+it represents a write-only value\n+to be used as a place-holder\n+where a variable is needed but the actual value is irrelevant.\n+It has uses beyond those we've seen already.\n+</p>\n+\n+<h3 id="blank_assign">The blank identifier in multiple assignment</h3>\n+\n+<p>\
+The use of a blank identifier in a <code>for</code> <code>range</code> loop is a\n+special case of a general situation: multiple assignment.\n+<p>\n+If an assignment requires multiple values on the left side,\n+but one of the values will not be used by the program,\n+a blank identifier on the left-hand-side of the\n+the assignment avoids the need\n+to create a dummy variable and makes it clear that the\n+value is to be discarded.\n+For instance, when calling a function that returns\n+a value and an error, but only the error is important,\n+use the blank identifier to discard the irrelevant value.\n+</p>\n+\n+<pre>\n+if _, err := os.Stat(path); os.IsNotExist(err) {\n+\tfmt.Printf("%s does not exist\\n", path)\n+}\n+</pre>\n+\n+<p>\n+Occasionally you'll see code that discards the error value in order\n+to ignore the error; this is terrible practice. Always check error returns;\n+they're provided for a reason.\n+</p>\n+\n+<pre>\n+// Bad! This code will crash if path does not exist.\n+fi, _ := os.Stat(path)\n+if fi.IsDir() {\n+    fmt.Printf("%s is a directory\\n", path)\n+}\n+</pre>\n+\n+<h3 id="blank_unused">Unused imports and variables</h3>\n+\n+<p>\n+It is an error to import a package or to declare a variable without using it.\n+Unused imports bloat the program and slow compilation,\n+while a variable that is initialized but not used is at least\n+a wasted computation and perhaps indicative of a\n+larger bug.\n+When a program is under active development, however,\n+unused imports and variables often arise and it can\n+be annoying to delete them just to have the compilation proceed,\n+only to have them be needed again later.\n+The blank identifier provides a workaround.\n+</p>\n+<p>\n+This half-written program is has two unused imports\n+(<code>fmt</code> and <code>io</code>)\n+and an unused variable (<code>fd</code>),\n+so it will not compile, but it would be nice to see if the\n+code so far is correct.\n+</p>\n+{{code "/doc/progs/eff_unused1.go" `/package/` `$`}}\n+<p>\n+To silence complaints about the unused imports, use a\n+blank identifier to refer to a symbol from the imported package.\n+Similarly, assigning the unused variable <code>fd</code>\n+to the blank identifier will silence the unused variable error.\n+This version of the program does compile.\n+</p>\n+{{code "/doc/progs/eff_unused2.go" `/package/` `$`}}\n+\n+<p>\n+By convention, the global declarations to silence import errors\n+should come right after the imports and be commented,\n+both to make them easy to find and as a reminder to clean things up later.\n+</p>\n+\n+<h3 id="blank_import">Import for side effect</h3>\n+\n+<p>\n+An unused import like <code>fmt</code> or <code>io</code> in the\n+previous example should eventually be used or removed:\n+blank assignments identify code as a work in progress.\n+But sometimes it is useful to import a package only for its\n+side effects, without any explicit use.\n+For example, during its <code>init</code> function,\n+the <code><a href="/pkg/net/http/pprof/">net/http/pprof</a></code>\n+package registers HTTP handlers that provide\n+debugging information. It has an exported API, but\n+most clients need only the handler registration and\n+access the data through a web page.\n+To import the package only for its side effects, rename the package\n+to the blank identifier:\n+</p>\n+<pre>\n+import _ "net/http/pprof"\n+</pre>\n+<p>\n+This form of import makes clear that the package is being\n+imported for its side effects, because there is no other possible\n+use of the package: in this file, it doesn't have a name.\n+(If it did, and we didn't use that name, the compiler would reject the program.)\n+</p>\n+\n+<h3 id="blank_implements">Interface checks</h3>\n+\n+<p>\n+As we saw in the discussion of <a href="#interfaces_and_types">interfaces</a> above,\n+a type need not declare explicitly that it implements an interface.\n+Instead, a type implements the interface just by implementing the interface's methods.\n+In practice, most interface conversions are static and therefore checked at compile time.\n+For example, passing an <code>*os.File</code> to a function\n+expecting an <code>io.Reader</code> will not compile unless\n+<code>*os.File</code> implements the <code>io.Reader</code> interface.\n+</p>\n+\n+<p>\n+Some interface checks do happen at run-time, though.\n+One instance is in the <code><a href="/pkg/encoding/json/">encoding/json</a></code>\n+package, which defines a <code><a href="/pkg/encoding/json/#Marshaler">Marshaler</a></code>\n+interface. When the JSON encoder receives a value that implements that interface,\n+the encoder invokes the value's marshaling method to convert it to JSON\n+instead of doing the standard conversion.\n+The encoder checks this property at run time with code like:\n+</p>\n+\n+<pre>\n+m, ok := val.(json.Marshaler)\n+</pre>\n+\n+<p>\n+If it's necessary only to ask whether a type implements an interface, without\n+actually using the interface itself, perhaps as part of an error check, use the blank\n+identifier to ignore the type-asserted value:\n+</p>\n+\n+<pre>\n+if _, ok := val.(json.Marshaler); ok {\n+    fmt.Printf("value %v of type %T implements json.Marshaler\\n", val, val)\n+}\n+</pre>\n+\n+<p>\n+One place this situation arises is when it is necessary to guarantee within the package implementing the type that\n+it it actually satisfies the interface.\n+If a type—for example,\n+<code><a href="/pkg/encoding/json/#RawMessage">json.RawMessage</a></code>—needs\n+a custom its JSON representation, it should implement\n+<code>json.Marshaler</code>, but there are no static conversions that would\n+cause the compiler to verify this automatically.\n+If the type inadvertently fails to satisfy the interface, the JSON encoder will still work,\n+but will not use the custom implementation.\n+To guarantee that the implementation is correct,\n+a global declaration using the blank identifier can be used in the package:\n+</p>\n+\n+<pre>\n+var _ json.Marshaler = (*RawMessage)(nil)\n+</pre>\n+<p>\n+In this declaration, the assignment involving a conversion of a\n+<code>*RawMessage</code> to a <code>Marshaler</code>\n+requires that <code>*RawMessage</code> implements <code>Marshaler</code>,\n+and that property will be checked at compile time.\n+Should the <code>json.Marshaler</code> interface change, this package\n+will no longer compile and we will be on notice that it needs to be updated.\n+</p>\n+\n+<p>\n+The appearance of the blank identifier in this construct indicates that\n+the declaration exists only for the type checking,\n+not to create a variable.\n+Don't do this for every type that satisfies an interface, though.\n+By convention, such declarations are only used\n+when there are no static conversions already present in the code,\n+which is a rare event.\n+</p>\n+\n+\n <h2 id="embedding">Embedding</h2>\n \n <p>\
@@ -3146,155 +3338,6 @@ 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>\
-\n-<p>\n-Go defines a special identifier <code>_</code>, called the <i>blank identifier</i>.\n-The blank identifier can be used in a declaration to avoid\n-declaring a name, and it can be used in an assignment to discard a value.\n-This definition makes it useful in a variety of contexts.\n-</p>\n-\n-<h3 id="blank_assign">Multiple assignment</h3>\n-\n-<p>\n-If an assignment requires multiple values on the left side,\n-but one of the values will not be used by the program,\n-using the blank identifier in the assignment avoids the need\n-to create a dummy variable.\n-We saw one example of this in the discussion of\n-<a href="#for">for loops</a> above.\n-</p>\n-<pre>\
-sum := 0\n-for _, value := range array {\n-    sum += value\n-}\
-</pre>\n-\n-<p>\n-Another common use is when calling a function that returns\n-a value and an error, but only the error is important.\n-</p>\n-<pre>\
-if _, err := os.Stat(path); os.IsNotExist(err) {\n-\tfmt.Printf("%s does not exist\\n", path)\n-}\
-</pre>\n-\n-<p>\
-A final use that is more common than it should be is to \n-discard the error from a function that is not expected to fail.\n-This is usually a mistake: when the function does fail, the code\n-will continue on and probably panic dereferencing a nil pointer.\n-</p>\n-<pre>\
-// Always check errors: this program crashes if path does not exist.\n-fi, _ := os.Stat(path)\n-fmt.Printf("%s is %d bytes\\n", path, fi.Size())\n-</pre>\n-\n-<h3 id="blank_unused">Unused imports and variables</h3>\n-\n-<p>\
-Go defines that it is an error to import a package without using it,\n-or to declare a variable without using its value.\n-Unused imports bloat a program and lengthen compiles unnecessarily;\n-a variable that is initialized but not used is at least\n-a wasted computation and perhaps indicative of a\n-larger bug.\n-Of course, both of these situations also arise in programs\n-that are under active development, as you test and refine\n-your code. \n-</p>\n-<p>\
-For example, in this program, there are two unused imports\n-(<code>fmt</code> and <code>io</code>)\n-and an unused variable (<code>greeting</code>).\n-</p>\n-{{code "/doc/progs/unused1.go" `/package/` `$`}}\n-<p>\
-Top-level blank declarations referring to the packages\n-will silence the unused import errors.\n-By convention, these declarations should come immediately after\n-the imports, as a reminder to clean things up later.\n-Similarly, assigning <code>greeting</code> to a blank identifier\n-will silence the unused variable error.\n-</p>\n-{{code "/doc/progs/unused2.go" `/package/` `$`}}\n-\n-<h3 id="blank_import">Import for side effect</h3>\n-\n-<p>\
-An unused import like <code>fmt</code> or <code>io</code> in the last section\n-should eventually be used or removed:\n-blank assignments identify code as a work in progress.\n-But sometimes it is useful to import a package only for its\n-side effects, without any explicit use.\n-For example, during its <code>init</code> function,\n-the <code><a href="/pkg/net/http/pprof/">net/http/pprof</a></code>\n-package registers HTTP handlers that provide useful\n-debugging information. It has an exported API too, but\n-most clients need only the handler registration.\n-In this situation, it is conventional to rename the package\n-to the blank identifier:\n-</p>\n-<pre>\
-import _ "net/http/pprof"\n-</pre>\n-<p>\
-This form of import makes clear that the package is being\n-imported for its side effects, because there is no other possible\n-use of the package: in this file, it doesn't have a name.\n-</p>\n-\n-<h3 id="blank_implements">Interface checks</h3>\n-\n-<p>\
-As we saw in the discussion of <a href="#interfaces_and_types">interfaces</a> above,\n-Go does not require a type to declare explicitly that it implements an interface.\n-It implements the interface by simply implementing the required methods.\n-This makes Go programs more lightweight and flexible, and it can avoid\n-unnecessary dependencies between packages. \n-Most interface conversions are static, visible to the compiler,\n-and therefore checked at compile time.\n-For example, passing an <code>*os.File</code> to a function\n-expecting an <code>io.Reader</code> will not compile unless\n-<code>*os.File</code> implements the <code>io.Reader</code> interface.\n-</p>\n-<p>\
-However, some types that are used only to satisfy dynamic interface checks.\n-For example, the <code><a href="/pkg/encoding/json/">encoding/json</a></code>\n-package defines a <code><a href="/pkg/encoding/json/#Marshaler">Marshaler</a></code>\n-interface. If the JSON encoder encounters a type implementing that interface,\n-the encoder will let the type convert itself to JSON instead of using the standard\n-conversion.\n-This check is done only at runtime, with code like:\n-</p>\n-<pre>\
-m, ok := val.(json.Marshaler)\n-</pre>\n-<p>\
-If a type—for example,\n-<code><a href="/pkg/encoding/json/#RawMessage">json.RawMessage</a></code>—intends\n-to customize its JSON representation, it should implement\n-<code>json.Marshaler</code>, but there are no static conversions that would\n-cause the compiler to verify this automatically.\n-A declaration can be used to add such a check:\n-</p>\n-<pre>\
-var _ json.Marshaler = (*RawMessage)(nil)\n-</pre>\n-<p>\
-As part of type-checking this static assignment of a\n-<code>*RawMessage</code> to a <code>Marshaler</code>,\n-the Go compiler will require that <code>*RawMessage</code> implements <code>Marshaler</code>.\n-Using the blank identifier here indicates that\n-the declaration exists only for the type checking,\n-not to create a variable.\n-Conventionally, such declarations are used only when there are\n-no static conversions already present in the code.\n-</p>\
 \
 <h2 id="web_server">A web server</h2>\
 \ndiff --git a/doc/progs/eff_unused1.go b/doc/progs/eff_unused1.go\
new file mode 100644\
index 0000000000..f990a19f77\
--- /dev/null\
+++ b/doc/progs/eff_unused1.go\
@@ -0,0 +1,18 @@\
+// skip\
+\
+package main\
+\
+import (\
+\t"fmt"\
+\t"io"\
+\t"log"\
+\t"os"\
+)\
+\
+func main() {\
+\tfd, err := os.Open("test.go")\
+\tif err != nil {\
+\t\tlog.Fatal(err)\
+\t}\
+\t// TODO: use fd.\
+}\
diff --git a/doc/progs/eff_unused2.go b/doc/progs/eff_unused2.go\
new file mode 100644\
index 0000000000..3e6e041c76\
--- /dev/null\
+++ b/doc/progs/eff_unused2.go\
@@ -0,0 +1,22 @@\
+// compile\
+\
+package main\
+\
+import (\
+\t"fmt"\
+\t"io"\
+\t"log"\
+\t"os"\
+)\
+\
+var _ = fmt.Printf // For debugging; delete when done.\
+var _ io.Reader    // For debugging; delete when done.\
+\
+func main() {\
+\tfd, err := os.Open("test.go")\
+\tif err != nil {\
+\t\tlog.Fatal(err)\
+\t}\
+\t// TODO: use fd.\
+\t_ = fd\
+}\
diff --git a/doc/progs/run b/doc/progs/run\
index da777f329b..71759c565e 100755\
--- a/doc/progs/run\
+++ b/doc/progs/run\
@@ -16,6 +16,7 @@ effective_go="\
 \teff_bytesize\
 \teff_qr\
 \teff_sequence\
+\teff_unused2\
 "\
 \n error_handling="\
 \ndiff --git a/doc/progs/unused1.go b/doc/progs/unused1.go\
deleted file mode 100644\
index 96a6d98a39..0000000000\
--- a/doc/progs/unused1.go\
+++ /dev/null\
@@ -1,12 +0,0 @@\
-// skip\
-\
-package main\
-\
-import (\
-\t"fmt"\
-\t"io"\
-)\
-\
-func main() {\
-\tgreeting := "hello, world"\
-}\
diff --git a/doc/progs/unused2.go b/doc/progs/unused2.go\
deleted file mode 100644\
index 5c5f9d74f4..0000000000\
--- a/doc/progs/unused2.go\
+++ /dev/null\
@@ -1,16 +0,0 @@\
-// compile\
-\
-package main\
-\
-import (\
-\t"fmt"\
-\t"io"\
-)\
-\
-var _ = fmt.Printf\
-var _ io.Reader\
-\
-func main() {\
-\tgreeting := "hello, world"\
-\t_ = greeting\
-}\

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

https://github.com/golang/go/commit/9e329a0d16a053a884ef02d4e17a50cafea61afc

元コミット内容

effective_go.html: move and rework the blank identifier section Also rename the relevant examples and make sure the working one compiles.

このコミットは、「Effective Go」ドキュメント内のブランク識別子に関するセクションを移動し、再構築することを目的としています。また、関連するコード例の名前を変更し、動作するように修正することも含まれています。

変更の背景

Go言語のブランク識別子 (_) は、その柔軟性から様々な場面で利用されますが、その用途は多岐にわたります。従来の「Effective Go」ドキュメントでは、ブランク識別子に関する説明が散在していたり、その重要性や多様な利用方法が十分に強調されていなかった可能性があります。

このコミットの背景には、Go言語の重要な概念であるブランク識別子について、より体系的かつ包括的な解説を提供し、読者の理解を深めるという意図があります。特に、Go言語のコンパイラが未使用のインポートや変数に対してエラーを出すという厳格なルールがある中で、ブランク識別子がいかに開発プロセスを円滑に進めるための「抜け道」として機能するか、そしてそれがどのような場面で適切に利用されるべきかを明確にすることが求められていました。

また、ドキュメント内のコード例が古くなっていたり、コンパイルエラーを起こす可能性があったため、それらを最新のGo言語の仕様に合わせて修正し、読者が実際に試せるようにすることも重要な目的でした。これにより、ドキュメントの信頼性と実用性が向上します。

前提知識の解説

Effective Go

「Effective Go」は、Go言語の公式ドキュメントの一部であり、Go言語を効果的かつ慣用的に記述するためのガイドラインとベストプラクティスを提供しています。Go言語の設計思想、基本的な構文、データ構造、並行処理、エラーハンドリングなど、幅広いトピックをカバーしており、Goプログラマーにとって必読のドキュメントとされています。このドキュメントは、単なる言語仕様の解説に留まらず、Goらしいコードの書き方や、Goの機能がどのように設計され、どのように利用されるべきかについての深い洞察を提供します。

ブランク識別子 (_)

Go言語におけるブランク識別子 (_) は、特殊な意味を持つ予約済みの識別子です。これは、値を破棄したり、名前を明示的に無視したりするために使用されます。Unixの/dev/nullファイルに書き込むようなもので、書き込み専用の値として機能し、変数が期待されるが実際の値は不要な場合のプレースホルダーとして利用されます。

ブランク識別子の主な用途は以下の通りです。

  1. 多値返却における値の破棄: Goの関数は複数の値を返すことができます。そのうちのいくつかが不要な場合、ブランク識別子を使用してその値を破棄できます。 例: _, err := os.Stat(path) (ファイル情報の取得は不要で、エラーのみに関心がある場合)
  2. for range ループにおけるインデックス/値の破棄: for range ループは、インデックスと値の2つの要素を返します。どちらか一方が不要な場合、ブランク識別子を使用できます。 例: for _, value := range array (インデックスが不要な場合)
  3. 未使用のインポートと変数の抑制: Goコンパイラは、未使用のインポートや変数をエラーとして扱います。これはコードの肥大化やコンパイル時間の増加を防ぐためですが、開発中のコードでは一時的に未使用の要素が発生することがあります。ブランク識別子を使用することで、これらのコンパイラエラーを抑制し、開発を継続できます。 例: var _ = fmt.Printf (デバッグ目的で一時的にfmtパッケージをインポートし、その関数を直接使用しない場合)
  4. 副作用のためのインポート: パッケージをインポートする主な目的が、そのパッケージのinit関数が実行されること(副作用)である場合、ブランク識別子を使用してインポートします。これにより、そのパッケージの他の要素がこのファイルで明示的に使用されないことを示し、コンパイラが未使用インポートとしてエラーを出すのを防ぎます。 例: import _ "net/http/pprof" (デバッグ用のHTTPハンドラを登録するためだけにインポートする場合)
  5. インターフェース実装のコンパイル時チェック: ある型が特定のインターフェースを実装していることをコンパイル時に保証するために、ブランク識別子を用いた型アサーションを使用することがあります。これは、特に静的な変換が存在しない場合に有用です。 例: var _ json.Marshaler = (*RawMessage)(nil) (json.RawMessageがjson.Marshalerインターフェースを実装していることをコンパイル時に確認する場合)

技術的詳細

このコミットは、doc/effective_go.html ファイルにおけるブランク識別子に関するセクションの構造と内容を根本的に変更しています。

主な変更点:

  1. セクションの移動と再構成:

    • 以前はドキュメントの後半に位置していた「Blank identifier」セクションが、より早い段階、特にfor rangeループやマップのセクションでブランク識別子が言及された直後に移動されました。これにより、読者はブランク識別子の概念に早期に触れ、その後の関連する説明をよりスムーズに理解できるようになります。
    • 新しいセクションはid="blank"というIDを持ち、ドキュメント内の他の箇所から参照されるようになっています。
    • セクションは、以下のサブセクションに細分化され、それぞれの用途が明確に説明されています。
      • The blank identifier in multiple assignment (多値代入におけるブランク識別子)
      • Unused imports and variables (未使用のインポートと変数)
      • Import for side effect (副作用のためのインポート)
      • Interface checks (インターフェースチェック)
  2. 内容の拡充と詳細化:

    • 各サブセクションにおいて、ブランク識別子の具体的な使用例と、それがなぜそのように使われるのかという背景が詳細に解説されています。
    • 特に、「未使用のインポートと変数」のセクションでは、Goコンパイラがこれらの要素をエラーとして扱う理由と、ブランク識別子がいかにその問題を回避し、開発中のコードをコンパイル可能にするかについて、具体的なコード例(eff_unused1.goeff_unused2.go)を用いて説明されています。
    • 「副作用のためのインポート」では、net/http/pprofパッケージを例に挙げ、そのinit関数がHTTPハンドラを登録するという副作用のためにインポートされるケースが説明されています。
    • 「インターフェースチェック」では、encoding/jsonパッケージのMarshalerインターフェースを例に、型がインターフェースを実装していることをコンパイル時に保証するためのvar _ Interface = (*Type)(nil)という慣用的な記述方法が導入されています。これは、静的な型変換がない場合に特に有用であり、インターフェースの変更があった際にコンパイルエラーとして検出できる利点があります。
  3. コード例の更新と整理:

    • 既存のdoc/progs/unused1.godoc/progs/unused2.goが削除され、新たにdoc/progs/eff_unused1.godoc/progs/eff_unused2.goが追加されました。これらの新しい例は、より現代的なGoのコードスタイルに沿っており、ブランク識別子の使用方法をより明確に示しています。
    • doc/progs/runスクリプトも更新され、新しいeff_unused2の例が実行対象に追加されています。これにより、ドキュメントのコード例が実際に動作することが保証されます。

この変更により、「Effective Go」ドキュメントは、ブランク識別子に関するより包括的で理解しやすい情報源となり、Goプログラマーがこの強力な機能を適切に利用するための手助けとなります。特に、Goの厳格なコンパイルルールの中で、ブランク識別子がいかに柔軟性を提供するかという点が強調されています。

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

このコミットのコアとなる変更は、主に以下のファイルに集中しています。

  1. doc/effective_go.html:

    • ブランク識別子に関する既存のセクション(id="blank")が削除され、ドキュメントのより早い位置に新しい、より詳細なセクションが追加されました。
    • 新しいセクションは、多値代入、未使用のインポートと変数、副作用のためのインポート、インターフェースチェックという4つの主要な用途に焦点を当て、それぞれに専用のサブセクションが設けられています。
    • 各サブセクションには、具体的なGoコードの例が埋め込まれています。
    • 既存のfor rangeループやマップに関する説明箇所で、ブランク識別子に言及する際に、新しい「ブランク識別子」セクションへのリンクが追加されました。
    • 全体として、238行が追加され、182行が削除されており、大幅な内容の再構築が行われたことがわかります。
  2. doc/progs/eff_unused1.go (新規追加):

    // skip
    
    package main
    
    import (
    	"fmt"
    	"io"
    	"log"
    	"os"
    )
    
    func main() {
    	fd, err := os.Open("test.go")
    	if err != nil {
    		log.Fatal(err)
    	}
    	// TODO: use fd.
    }
    

    このファイルは、未使用のインポート(fmt, io)と未使用の変数(fd)があるためにコンパイルエラーとなるGoプログラムの例として追加されました。コメントの// skipは、このコードが意図的にコンパイルされないことを示唆しています。

  3. doc/progs/eff_unused2.go (新規追加):

    // compile
    
    package main
    
    import (
    	"fmt"
    	"io"
    	"log"
    	"os"
    )
    
    var _ = fmt.Printf // For debugging; delete when done.
    var _ io.Reader    // For debugging; delete when done.
    
    func main() {
    	fd, err := os.Open("test.go")
    	if err != nil {
    		log.Fatal(err)
    	}
    	// TODO: use fd.
    	_ = fd
    }
    

    このファイルは、eff_unused1.goと同じコードベースですが、ブランク識別子を使用して未使用のインポートと変数のコンパイラエラーを抑制する方法を示しています。var _ = fmt.Printfvar _ io.Reader、そして_ = fdといった記述がその例です。コメントの// compileは、このコードがコンパイル可能であることを示しています。

  4. doc/progs/unused1.go (削除) および doc/progs/unused2.go (削除):

    • これらは、新しいeff_unused1.goeff_unused2.goに置き換えられた古いコード例です。
  5. doc/progs/run:

    • このシェルスクリプトは、ドキュメント内のコード例をテストするために使用されます。新しいeff_unused2が実行対象のリストに追加されました。

これらの変更は、Go言語のドキュメントの品質と正確性を向上させ、特にブランク識別子という重要な概念に対する読者の理解を深めることを目的としています。

コアとなるコードの解説

このコミットの核となるのは、doc/effective_go.htmlにおけるブランク識別子に関する説明の再構築と詳細化です。

以前のバージョンでは、ブランク識別子の説明はドキュメントの後半にあり、その用途も簡潔にまとめられていました。しかし、このコミットでは、ブランク識別子がGo言語の様々な場面で登場する重要な概念であるという認識に基づき、その説明をドキュメントのより早い段階に移動させ、さらにその多様な利用方法を網羅的に解説しています。

effective_go.htmlの変更点:

  • セクションの移動: for rangeループやマップの説明の直後にブランク識別子のセクションを配置することで、読者がこれらの基本的な構文でブランク識別子に遭遇した際に、すぐにその詳細な説明を参照できるようにしています。
  • 網羅的な用途の解説:
    • 多値代入: _, err := os.Stat(path)のように、関数が返す複数の値のうち、一部が不要な場合にブランク識別子を使ってその値を破棄する方法を説明しています。特に、エラーを無視するためにブランク識別子を使うのは「ひどい習慣」であると警告し、常にエラーをチェックすることの重要性を強調しています。
    • 未使用のインポートと変数: Goコンパイラが未使用のインポートや変数をエラーとする厳格なルールがある中で、開発中に一時的にこれらを抑制するための「抜け道」としてブランク識別子を使う方法を解説しています。var _ = fmt.Printf_ = fdといった記述がその典型例です。これにより、コードが未完成な状態でもコンパイルを進めることができ、開発の効率を向上させます。ただし、これらは一時的な措置であり、最終的には不要なインポートや変数を削除すべきであるという慣習も示されています。
    • 副作用のためのインポート: import _ "net/http/pprof"のように、パッケージのinit関数が実行されること(副作用)が主な目的で、そのパッケージの他の要素を直接使用しない場合にブランク識別子を使う方法を説明しています。これにより、コンパイラが未使用インポートとしてエラーを出すのを防ぎ、コードの意図を明確に伝えます。
    • インターフェースチェック: var _ json.Marshaler = (*RawMessage)(nil)のように、ある型が特定のインターフェースを実装していることをコンパイル時に保証するための慣用的な記述方法を導入しています。これは、静的な型変換が存在しない場合に特に有用で、インターフェースの定義が変更された際にコンパイルエラーとして検出できるため、コードの堅牢性を高めます。

新しいコード例 (eff_unused1.go, eff_unused2.go) の役割:

これらの新しいGoプログラムは、effective_go.htmlの「未使用のインポートと変数」セクションで具体例として使用されています。

  • eff_unused1.goは、意図的に未使用のインポートと変数を含んでおり、Goコンパイラがエラーを出すことを示しています。これは、読者がGoのコンパイルルールを理解するための出発点となります。
  • eff_unused2.goは、eff_unused1.goと同じ問題を持つコードですが、ブランク識別子を適切に使用することで、コンパイラエラーを回避し、コードをコンパイル可能にする方法を示しています。これにより、読者はブランク識別子が開発プロセスにおいてどのように役立つかを実践的に学ぶことができます。

これらの変更は、Go言語の学習者や開発者にとって、ブランク識別子の理解を深め、より効果的なGoコードを書くための重要なリソースとなります。

関連リンク

参考にした情報源リンク