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

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

このコミットは、Go言語の仕様書 doc/go_spec.html に、アドレス演算子 & の振る舞いに関する重要な変更、特に評価対象の式がパニックを引き起こす場合の挙動について追記するものです。具体的には、&x の評価が x の評価によってランタイムパニックを引き起こす場合、&x 自体もパニックを引き起こすことを明確にしています。

コミット

commit 5ce78b7cd281b187a06dcdb9ea9d1de8419db297
Author: Russ Cox <rsc@golang.org>
Date:   Thu Aug 15 14:33:26 2013 -0400

    spec: &x panics if x does
    
    See golang.org/s/go12nil for the extended version.
    
    R=golang-dev, r, adonovan
    CC=golang-dev
    https://golang.org/cl/12964043

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

https://github.com/golang/go/commit/5ce78b7cd281b187a06dcdb9ea9d1de8419db297

元コミット内容

spec: &x panics if x does

See golang.org/s/go12nil for the extended version.

R=golang-dev, r, adonovan
CC=golang-dev
https://golang.org/cl/12964043

変更の背景

このコミットは、Go 1.2における重要な仕様変更、特に「Go 1.2 Field Selectors and Nil Checks」という設計ドキュメント (golang.org/s/go12nil) に基づいています。Go言語では、ポインタのデリファレンス(*x)がnilポインタに対して行われた場合にランタイムパニックが発生することは明確でした。しかし、アドレス演算子 & を使用して、nilポインタのフィールドのアドレスを取得しようとする場合(例: &x.Field、ここで x はnilポインタ)、その挙動が曖昧でした。

Go 1.2以前は、このようなケースで一貫性のない、あるいは予期せぬ動作が発生する可能性がありました。例えば、&x.Field がnilポインタに対して実行された場合、フィールドのオフセットによっては、有効なポインタのように見えてしまうことがあり、これが後続のコードで問題を引き起こす可能性がありました。この曖昧さは、開発者が予測可能なコードを書く上で障害となり、潜在的なバグの温床となっていました。

このコミットは、このような曖昧さを解消し、Go言語の安全性と予測可能性を高めることを目的としています。&x の評価が x の評価によってパニックを引き起こす場合、&x 自体もパニックを引き起こすという明確なルールを導入することで、開発者はnilポインタの扱いに関してより堅牢なコードを記述できるようになります。

前提知識の解説

  • アドレス演算子 (&): Go言語において、変数のメモリアドレスを取得するために使用される演算子です。例えば、&v は変数 v のアドレスを返します。
  • ポインタ (*T): メモリアドレスを格納する変数型です。*T は型 T の値へのポインタを表します。
  • デリファレンス演算子 (*): ポインタが指すメモリアドレスに格納されている値を取得するために使用される演算子です。例えば、*p はポインタ p が指す値を返します。
  • ランタイムパニック (Run-time panic): Goプログラムの実行中に発生する回復不可能なエラーです。例えば、nilポインタのデリファレンス、配列の範囲外アクセスなどがパニックを引き起こします。パニックが発生すると、通常のプログラムフローは中断され、defer関数が実行された後、プログラムは終了します(recoverされない限り)。
  • Go言語の仕様 (Go Language Specification): Go言語の構文、セマンティクス、および標準ライブラリの動作を定義する公式ドキュメントです。言語の挙動に関する最終的な権威となります。
  • golang.org/s/go12nil: これは、Go 1.2におけるフィールドセレクタとnilチェックに関する設計ドキュメントへのショートリンクです。Go言語の重要な変更や新機能は、通常、このような設計ドキュメントで提案され、コミュニティのレビューを経て実装されます。このドキュメントは、nilポインタに対するフィールド選択の挙動の変更について詳細に説明しています。

技術的詳細

このコミットの技術的な核心は、Go言語の仕様書におけるアドレス演算子 & のセクションに、以下の文言を追加することです。

If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.

これは、アドレス演算子 & のオペランド x の評価がランタイムパニックを引き起こす場合、&x 自体の評価も同様にランタイムパニックを引き起こすことを明確に規定しています。

具体的な例として、コミットで追加されたコードスニペットを見てみましょう。

var x *int = nil
*x   // causes a run-time panic
&*x  // causes a run-time panic
  1. var x *int = nil: xint 型へのポインタとして宣言され、nil で初期化されます。つまり、x は何も指していません。
  2. *x: ここで x をデリファレンスしようとすると、nil ポインタのデリファレンスとなり、Go言語の既存のルールに従ってランタイムパニックが発生します。
  3. &*x: この行がこのコミットの変更のポイントです。以前は、&*x の挙動が曖昧でした。しかし、このコミットによって追加された仕様により、*x の評価がパニックを引き起こすため、&*x の評価もパニックを引き起こすことが保証されます。

この変更は、Go言語の型システムとランタイムの整合性を高めるものです。アドレス演算子 & は、そのオペランドが有効なメモリ位置を指していることを前提とします。もしオペランドの評価自体が不正な状態(この場合はnilポインタのデリファレンス)を引き起こすのであれば、そのアドレスを取得しようとする操作もまた不正であると見なされるべきだという論理に基づいています。

これにより、開発者はnilポインタのデリファレンスがアドレス演算子の内部で発生した場合でも、予測可能なパニック挙動を期待できるようになり、デバッグが容易になり、より堅牢なプログラムを構築できるようになります。

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

変更は doc/go_spec.html ファイルに対して行われています。

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -3287,7 +3287,10 @@ or an array indexing operation of an addressable array.
 As an exception to the addressability requirement, <code>x</code> may also be a
 (possibly parenthesized)
 <a href="#Composite_literals">composite literal</a>.
+If the evaluation of <code>x</code> would cause a <a href="#Run_time_panics">run-time panic</a>,
+then the evaluation of <code>&x</code> does too.
 </p>
+
 <p>
 For an operand <code>x</code> of pointer type <code>*T</code>, the pointer
 indirection <code>*x</code> denotes the value of type <code>T</code> pointed
@@ -3302,6 +3305,10 @@ will cause a <a href="#Run_time_panics">run-time panic</a>.
 &amp;Point{2, 3}
 *p
 *pf(x)
+
+var x *int = nil
+*x   // causes a run-time panic
+&*x  // causes a run-time panic
 </pre>

具体的には、以下の2点が変更されています。

  1. アドレス演算子 & の説明セクションに、パニックに関する新しいルールが追加されました。
    If the evaluation of <code>x</code> would cause a <a href="#Run_time_panics">run-time panic</a>,
    then the evaluation of <code>&x</code> does too.
    
  2. この新しいルールを説明するための具体的なコード例が追加されました。
    var x *int = nil
    *x   // causes a run-time panic
    &*x  // causes a run-time panic
    

コアとなるコードの解説

追加されたHTMLのテキストは、Go言語の仕様書におけるアドレス演算子 & のセクションに、その挙動に関する重要な補足説明を加えるものです。

If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.

この一文は、&x という式を評価する際に、まず x の部分が評価され、もしその x の評価自体がランタイムパニックを引き起こすような状況であれば、その &x という式全体もランタイムパニックを引き起こす、ということを明確にしています。

例えば、x*p のようなポインタのデリファレンスであり、pnil である場合、*p の評価はランタイムパニックを引き起こします。この新しい仕様により、&*p という式も同様にパニックを引き起こすことが保証されます。これは、アドレス演算子がそのオペランドが有効なメモリ位置を指していることを前提としているため、オペランドの評価が不正な状態(nilポインタのデリファレンス)を引き起こすのであれば、そのアドレスを取得しようとする操作もまた不正であると見なされるべきだという論理に基づいています。

追加されたコード例は、この新しいルールを具体的に示しています。

var x *int = nil
*x   // causes a run-time panic
&*x  // causes a run-time panic
  • var x *int = nil: xnil ポインタです。
  • *x: nil ポインタのデリファレンスは、Goの既存のルールによりパニックを引き起こします。
  • &*x: ここがポイントです。& 演算子のオペランドは *x です。上記の行で示されているように、*x の評価はパニックを引き起こします。したがって、この新しい仕様により、&*x の評価もパニックを引き起こすことが保証されます。

この変更は、Go言語の予測可能性と堅牢性を向上させ、開発者がnilポインタの扱いに関してより安全なコードを記述できるようにするための重要なステップです。

関連リンク

  • Go 1.2 Field Selectors and Nil Checks (golang.org/s/go12nil): このコミットの背景にある設計ドキュメントです。nilポインタに対するフィールド選択の挙動の変更について詳細に説明されています。
  • Go issue 4238: この仕様変更に関する議論が行われたGoのIssueトラッカーのエントリです。
  • Gerrit Change 12964043: このコミットに対応するGerritの変更セットです。

参考にした情報源リンク