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

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

このコミットは、Go言語の公式FAQドキュメント(doc/go_faq.html)に、nilインターフェースとnilポインタに関する新しいエントリを追加するものです。具体的には、「なぜnilエラー値がnilと等しくないのか?」というよくある疑問に対する詳細な説明が加えられています。これは、Go言語におけるインターフェースの内部実装と、nil値の扱いに関する重要な概念を明確にするための変更です。

コミット

commit 1e0f97ac6544ab3b7cf76e9c9d62e8c2a60bcdb6
Author: Rob Pike <r@golang.org>
Date:   Fri Feb 17 16:27:17 2012 +1100

    faq: add entry about nil interfaces vs. nil pointers
    
    Fixes #2778.
    
    R=bradfitz, rsc, iant, adg
    CC=golang-dev
    https://golang.org/cl/5672078

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

https://github.com/golang/go/commit/1e0f97ac6544ab3b7cf76e9c9d62e8c2a60bcdb6

元コミット内容

このコミットは、doc/go_faq.htmlファイルに以下のHTMLスニペットを追加しています。

<h3 id="nil_error">
Why is my nil error value not equal to nil?
</h3>

<p>
Under the covers, interfaces are implemented as two elements, a type and a value.
The value, called the interface's dynamic value,
is an arbitrary concrete value and the type is that of the value.
For the <code>int</code> value 3, an interface value contains,
schematically, (<code>int</code>, <code>3</code>).
</p>

<p>
An interface value is <code>nil</code> only if the inner value and type are both unset,
(<code>nil</code>, <code>nil</code>).
In particular, a <code>nil</code> interface will always hold a <code>nil</code> type.
If we store a pointer of type <code>*int</code> inside
an interface value, the inner type will be <code>*int</code> regardless of the value of the pointer:
(<code>*int</code>, <code>nil</code>).
Such an interface value will therefore be non-<code>nil</code>
<em>even when the pointer inside is</em> <code>nil</code>.
</p>

<p>
This situation can be confusing, and often arises when a <code>nil</code> value is
stored inside an interface value such as an <code>error</code> return:
</p>

<pre>
func returnsError() error {
	var p *MyError = nil
	if bad() {
		p = ErrBad
	}
	return p // Will always return a non-nil error.
}
</pre>

<p>
If all goes well, the function returns a <code>nil</code> <code>p</code>,
so the return value is an <code>error</code> interface
value holding (<code>*MyError</code>, <code>nil</code>).
This means that if the caller compares the returned error to <code>nil</code>,
it will always look as if there was an error even if nothing bad happened.
To return a proper <code>nil</code> <code>error</code> to the caller,
the function must return an explicit <code>nil</code>:
</p>


<pre>
func returnsError() error {
	if bad() {
		return ErrBad
	}
	return nil
}
</pre>

<p>
It's a good idea for functions
that return errors always to use the <code>error</code> type in
their signature (as we did above) rather than a concrete type such
as <code>*MyError</code>, to help guarantee the error is
created correctly. As an example,
<a href="/pkg/os/#Open"><code>os.Open</code></a>
returns an <code>error</code> even though, if not <code>nil</code>,
it's always of concrete type
<a href="/pkg/os/#PathError"><code>*os.PathError</code></a>.
</p>

<p>
Similar situations to those described here can arise whenever interfaces are used.
Just keep in mind that if any concrete value
has been stored in the interface, the interface will not be <code>nil</code>.
For more information, see
<a href="http://blog.golang.org/2011/09/laws-of-reflection.html">this blog post</a>.
</p>

変更の背景

このコミットは、Go言語のIssue #2778「faq: add entry about nil interfaces vs. nil pointers」を解決するために行われました。このIssueは、Goプログラマーが頻繁に遭遇する混乱の源である「nilポインタがインターフェースにラップされると、そのインターフェース値自体はnilではない」という挙動について、公式ドキュメントで明確な説明が求められていたことを示しています。

Go言語のインターフェースは強力な機能ですが、その内部実装とnil値の相互作用は、特にC++やJavaなどの他の言語の経験がある開発者にとっては直感的ではない場合があります。この混乱は、エラーハンドリングの際に特に顕著になります。関数が特定の型(例: *MyError)のnilポインタを返しても、それがerrorインターフェース型として返されると、呼び出し元でerr != nilと評価されてしまうため、予期せぬバグにつながることがありました。

このFAQエントリの追加は、このような一般的な誤解を解消し、開発者がGoのインターフェースとエラーハンドリングをより正確に理解し、適切に利用できるようにすることを目的としています。

前提知識の解説

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

  1. インターフェース (Interfaces): Go言語のインターフェースは、メソッドのシグネチャの集合を定義する型です。ある型がインターフェースのすべてのメソッドを実装していれば、その型はそのインターフェースを満たしていると見なされます。Goのインターフェースは、動的なディスパッチ(ポリモーフィズム)を実現するための主要なメカニズムです。

  2. nil: Goにおけるnilは、ポインタ、チャネル、関数、インターフェース、マップ、スライスなど、いくつかの型のゼロ値(初期値)を表します。nilは「値がない」状態を示しますが、その意味合いは型によって微妙に異なります。

  3. ポインタ (Pointers): ポインタは、変数のメモリアドレスを保持する変数です。Goでは、C/C++のようなポインタ演算はできませんが、値の参照渡しや、構造体のメソッドレシーバとして使用されます。nilポインタは、何も指していないポインタを意味します。

  4. インターフェースの内部表現: Goのインターフェース値は、内部的に2つの要素で構成されています。

    • 型 (Type): インターフェースに格納されている具体的な値の型(動的型)。
    • 値 (Value): インターフェースに格納されている具体的な値(動的値)。

    インターフェース値がnilであると判断されるのは、この「型」と「値」の両方がnilである場合のみです。つまり、(type: nil, value: nil)の状態です。

技術的詳細

追加されたFAQエントリは、Goのインターフェースの内部構造とnilの挙動について、以下の重要な点を詳細に説明しています。

  • インターフェースの二要素: インターフェースは、動的な型と動的な値のペアとして実装されています。例えば、int型の値3がインターフェースに格納されると、それは概念的に(int, 3)として表現されます。

  • 真のnilインターフェース: インターフェース値がnilと評価されるのは、その内部の動的な型と動的な値の両方がnilである場合のみです。つまり、(nil, nil)の状態です。

  • nilポインタと非nilインターフェース: ここが混乱の核心です。もし*MyErrorのようなポインタ型がインターフェースに格納された場合、たとえそのポインタ自体がnilであっても、インターフェースの動的な型は*MyErrorとなります。この場合、インターフェースは(type: *MyError, value: nil)という状態になります。この状態では、インターフェースの「型」要素がnilではないため、インターフェース値全体としてはnilではないと判断されます。

  • エラーハンドリングの落とし穴: この挙動は、特にエラーを返す関数で問題となります。関数が*MyError型のnilポインタを返しても、それがerrorインターフェースとして返されると、呼び出し元でif err != nilというチェックが常にtrueになってしまい、エラーがないにもかかわらずエラーとして扱われてしまいます。

  • 正しいnilエラーの返し方: 関数が真にnilのエラーを返すためには、具体的な型のnilポインタをインターフェースにラップして返すのではなく、明示的にreturn nilと記述する必要があります。これにより、インターフェースの動的な型と値の両方がnilとなり、呼び出し元で正しくnilと評価されます。

  • エラーシグネチャの推奨: エラーを返す関数では、戻り値の型を具体的な型(例: *MyError)ではなく、常にerrorインターフェース型にすることが推奨されます。これにより、nilエラーの作成が正しく行われることを保証しやすくなります。os.Openの例が挙げられており、これはerrorを返しますが、非nilの場合は常に*os.PathErrorという具体的な型を持つことが示されています。

  • 一般的な原則: このnilインターフェースの挙動は、error型に限らず、Goのインターフェース全般に当てはまります。インターフェースに何らかの具体的な値(たとえそれがnilポインタであっても)が格納されている場合、そのインターフェース値はnilではないと見なされます。

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

変更はdoc/go_faq.htmlファイルに対して行われ、既存のFAQエントリの後に新しいセクションが追加されています。具体的には、id="nil_error"を持つ<h3>タグから始まり、Goのインターフェースにおけるnilの挙動、具体的なコード例、そして正しいエラーハンドリングの推奨事項を説明する一連の<p>タグと<pre>タグが追加されています。

この変更は、HTMLドキュメントへの純粋なコンテンツ追加であり、Go言語のランタイムやコンパイラの動作を変更するものではありません。既存のドキュメントに、Goの重要な概念に関する説明を補強するものです。

コアとなるコードの解説

追加されたHTMLコードは、Goのインターフェースの「型と値」という二重構造を視覚的に説明し、なぜnilポインタがnilではないインターフェース値になるのかを段階的に解説しています。

  1. インターフェースの構造説明: 最初の段落で、インターフェースが「型」と「値」の2つの要素で構成されていることを説明します。
  2. nilインターフェースの定義: 次の段落で、真のnilインターフェースは、その内部の型と値の両方がnilである場合のみであることを明確にします。そして、*int型のnilポインタがインターフェースに格納された場合、型は*intとなり、値はnilとなるため、インターフェース全体としてはnilではないと説明します。
  3. 問題のあるコード例: returnsError()関数の最初の例では、*MyError型のnilポインタperrorインターフェースとして返すと、呼び出し元でnilチェックが失敗する(常に非nilと評価される)ことを示します。
  4. 正しいコード例: returnsError()関数の2番目の例では、エラーがない場合に明示的にreturn nilとすることで、真のnilエラーインターフェースを返す方法を示します。
  5. ベストプラクティス: エラーを返す関数のシグネチャには常にerror型を使用し、具体的な型は避けるべきであるという推奨事項を述べます。os.Openの例を挙げて、この原則を補強します。
  6. 一般化と追加情報: 最後に、このnilインターフェースの挙動がerror型に限定されず、インターフェース全般に当てはまることを強調し、詳細についてはRob Pikeによる「Laws of Reflection」というブログ記事を参照するよう促しています。

このFAQエントリは、Go言語の設計思想と、それがどのように具体的なコードの挙動に現れるかを理解するための、非常に重要な情報源となっています。

関連リンク

  • Go Blog Post: Laws of Reflection: http://blog.golang.org/2011/09/laws-of-reflection.html このブログ記事は、Goのインターフェースとリフレクションの仕組みについて、より深く掘り下げた解説を提供しており、本FAQエントリの理解をさらに深めるのに役立ちます。

参考にした情報源リンク