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

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

このコミットは、Go言語における組み込み関数 len および cap がポインタ型に対して自動的に間接参照(dereference)を行う機能のテストを追加するものです。具体的には、mapstringarrayslice の各型へのポインタに対して lencap を適用した場合の挙動を検証しています。

コミット

commit cbd08ed2615e9393460760b8d3f2bd352b7e95f5
Author: Russ Cox <rsc@golang.org>
Date:   Fri Jan 9 15:38:01 2009 -0800

    test of automatic indirect
    
    R=r
    DELTA=93  (93 added, 0 deleted, 0 changed)
    OCL=22458
    CL=22461

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

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

元コミット内容

このコミットは、test/indirect.go という新しいテストファイルを追加しています。このファイルは、mapstringarrayslice の各型へのポインタに対して len および cap 関数がどのように振る舞うかを検証するためのものです。特に、これらの関数がポインタを自動的に間接参照して、その指し示す値の長さや容量を返すことを確認しています。また、nil ポインタに対してこれらの関数を呼び出した場合に、コンパイルエラーではなく実行時パニックとなることをも検証しています。

変更の背景

Go言語は、設計当初からシンプルさと実用性を重視していました。lencap のような組み込み関数は、様々なデータ構造のサイズや容量を取得するために頻繁に使用されます。もしこれらの関数がポインタ型に対して直接適用できない場合、開発者は常に明示的な間接参照演算子 * を使用してポインタをデリファレンスする必要がありました。これはコードの冗長性を招き、可読性を低下させる可能性があります。

この「自動間接参照」の導入は、Go言語の設計哲学である「実用性」と「簡潔さ」に合致するものです。これにより、開発者はポインタを介してデータ構造の長さや容量を取得する際に、より自然なコードを書くことができるようになります。このコミットは、この重要な言語機能が正しく動作することを保証するためのテストケースを追加するものです。

前提知識の解説

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

  1. ポインタ (Pointers): Go言語におけるポインタは、変数のメモリアドレスを保持する変数です。*T の形式で宣言され、& 演算子で変数のアドレスを取得し、* 演算子でポインタが指し示す値にアクセス(間接参照)します。 例: var x int = 10; p := &x; fmt.Println(*p)

  2. 組み込み関数 len: len は、Go言語に組み込まれている関数で、様々なデータ構造の「長さ」を返します。

    • 文字列 (string): 文字列のバイト数を返します。
    • 配列 (array): 配列の要素数を返します。
    • スライス (slice): スライスの現在の要素数を返します。
    • マップ (map): マップ内のキーと値のペアの数を返します。
    • チャネル (channel): チャネル内のキューに入れられた要素の数を返します。
  3. 組み込み関数 cap: cap は、Go言語に組み込まれている関数で、スライス、配列、チャネルの「容量」を返します。

    • 配列 (array): 配列の要素数を返します(len と同じ)。
    • スライス (slice): スライスの基底配列の容量を返します。スライスが拡張できる最大要素数を示します。
    • チャネル (channel): チャネルのバッファ容量を返します。
  4. 自動間接参照 (Automatic Dereferencing): Go言語の特定のコンテキスト(特にメソッド呼び出しや一部の組み込み関数)では、ポインタ型に対して操作を行う際に、コンパイラが自動的にポインタを間接参照して、その指し示す値に対して操作を適用します。このコミットは、lencap 関数がこの自動間接参照の恩恵を受けることをテストしています。

  5. nil ポインタ: Go言語では、ポインタが何も指し示していない状態を nil と表現します。nil ポインタを間接参照しようとすると、通常は実行時パニック (runtime panic) が発生します。このテストでは、lencapnil ポインタに対して呼び出された場合に、コンパイルエラーではなくパニックになることを確認しています。これは、型チェックの段階では有効な呼び出しと見なされるが、実行時に値が存在しないためにエラーとなる、というGoの設計思想を反映しています。

技術的詳細

このコミットで追加された test/indirect.go ファイルは、len および cap 関数がポインタ型に対してどのように動作するかを詳細に検証しています。

Go言語のコンパイラは、lencap の引数がポインタ型である場合、そのポインタが指し示す基底の型(mapstringarrayslice)に対してこれらの関数を適用するように自動的に処理します。これは、コンパイラが内部的に len(*p)cap(*p) のように変換していると考えることができます。

テストファイルでは、以下のシナリオが検証されています。

  1. nil ポインタに対する len/cap の呼び出し (crash 関数): var m1 *map[string]int のように nil で初期化されたポインタに対して len(m1) を呼び出しています。コメントには「would crash but should type check」とあり、これはコンパイル時には型エラーとならず、実行時に nil ポインタのデリファレンスによってパニックが発生することを意図しています。これは、Goの型システムが、ポインタが nil でないことを保証するのではなく、その型が正しいことを保証することを示しています。

  2. nil ポインタに対する len/cap の呼び出し (nocrash 関数):

    • map の例: var m2 *map[string]int = &m0 のように、初期化されたマップへのポインタに対して len(m2) を呼び出し、期待される結果(len(m0) と同じ)が得られることを確認しています。
    • string の例: var s4 *string = &s3 のように、初期化された文字列へのポインタに対して len(s4) を呼び出し、期待される結果が得られることを確認しています。
    • array の例: var a2 *[10]int = &a0 のように、初期化された配列へのポインタに対して len(a2) を呼び出し、期待される結果が得られることを確認しています。
    • slice の例: var b4 *[]int = &b3 のように、初期化されたスライスへのポインタに対して len(b4) および cap(b4) を呼び出し、期待される結果が得られることを確認しています。

これらのテストは、lencap がポインタを自動的に間接参照し、その指し示す値の長さや容量を正しく返すというGo言語のセマンティクスを確立し、検証するために不可欠です。

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

このコミットでは、test/indirect.go という新しいファイルが追加されています。

--- /dev/null
+++ b/test/indirect.go
@@ -0,0 +1,97 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG indirect
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+var m0 map[string]int
+var m1 *map[string]int
+var m2 *map[string]int = &m0
+var m3 map[string]int = map[string]int{"a": 1}
+var m4 *map[string]int = &m3
+
+var s0 string
+var s1 *string
+var s2 *string = &s0
+var s3 string = "a"
+var s4 *string = &s3
+
+var a0 [10]int
+var a1 *[10]int
+var a2 *[10]int = &a0
+
+var b0 []int
+var b1 *[]int
+var b2 *[]int = &b0
+var b3 []int = []int{1, 2, 3}
+var b4 *[]int = &b3
+
+func crash()
+{
+	// these uses of nil pointers
+	// would crash but should type check
+	println("crash",
+		len(m1)+
+		len(s1)+
+		len(a1)+
+		len(b1)+
+		cap(b1));
+}
+
+func nocrash()
+{
+	// this is spaced funny so that
+	// the compiler will print a different
+	// line number for each len call if
+	// it decides there are type errors.
+	// it might also help in the traceback.
+	x :=
+		len(m0)+
+		len(m2)+
+		len(m3)+
+		len(m4);
+	if x != 2 {
+		panicln("wrong maplen");
+	}
+
+	x =
+		len(s0)+
+		len(s2)+
+		len(s3)+
+		len(s4);
+	if x != 2 {
+		panicln("wrong stringlen");
+	}
+
+	x =
+		len(a0)+
+		len(a2);
+	if x != 20 {
+		panicln("wrong arraylen");
+	}
+
+	x =
+		len(b0)+
+		len(b2)+
+		len(b3)+
+		len(b4);
+	if x != 6 {
+		panicln("wrong slicelen");
+	}
+
+	x =
+		cap(b0)+
+		cap(b2)+
+		cap(b3)+
+		cap(b4);
+	if x != 6 {
+		panicln("wrong slicecap\");
+	}
+}
+
+func main()
+{
+	nocrash();
+}

コアとなるコードの解説

test/indirect.go は、Go言語のテストフレームワークを使用せず、直接コンパイルと実行を行うシンプルなテストスクリプトです。

  1. 変数宣言: m0 から b4 までのグローバル変数が宣言されています。これらは、mapstringarrayslice の各型、およびそれらへのポインタ型を含んでいます。特に、m1, s1, a1, b1nil ポインタとして初期化されており、m2, s2, a2, b2, b4 はそれぞれ対応する非ポインタ変数へのポインタとして初期化されています。

  2. crash() 関数: この関数は、nil ポインタに対して lencap を呼び出すことを意図しています。 println ステートメント内で len(m1)len(s1)len(a1)len(b1)cap(b1) を呼び出しています。 コメントにあるように、これらの呼び出しはコンパイル時には型チェックを通過しますが、実行時には nil ポインタのデリファレンスによってパニックを引き起こすはずです。この関数は main から直接呼び出されていませんが、テストの意図を明確にするために存在しています。

  3. nocrash() 関数: この関数は、非nil の変数およびポインタに対して lencap を呼び出し、その結果が期待通りであることを検証します。

    • map の長さの検証: len(m0) (初期化されていないマップは長さ0)、len(m2) (m0へのポインタ)、len(m3) (要素が1つのマップ)、len(m4) (m3へのポインタ) の合計が 0 + 0 + 1 + 1 = 2 になることを確認しています。
    • string の長さの検証: len(s0) (空文字列は長さ0)、len(s2) (s0へのポインタ)、len(s3) (文字列"a"は長さ1)、len(s4) (s3へのポインタ) の合計が 0 + 0 + 1 + 1 = 2 になることを確認しています。
    • array の長さの検証: len(a0) (要素数10の配列)、len(a2) (a0へのポインタ) の合計が 10 + 10 = 20 になることを確認しています。
    • slice の長さの検証: len(b0) (nilスライスは長さ0)、len(b2) (b0へのポインタ)、len(b3) (要素数3のスライス)、len(b4) (b3へのポインタ) の合計が 0 + 0 + 3 + 3 = 6 になることを確認しています。
    • slice の容量の検証: cap(b0) (nilスライスは容量0)、cap(b2) (b0へのポインタ)、cap(b3) (要素数3のスライスは容量3)、cap(b4) (b3へのポインタ) の合計が 0 + 0 + 3 + 3 = 6 になることを確認しています。 各検証の後には panicln を用いて、期待される値と異なる場合にテストが失敗することを示しています。
  4. main() 関数: nocrash() 関数のみを呼び出しています。これにより、nil ポインタのテストケース (crash() 関数) は実行されず、自動間接参照が正しく機能する場合のテストのみが実行されます。

このテストファイルは、Go言語のコンパイラが lencap の引数としてポインタを受け取った際に、自動的にそのポインタを間接参照して、指し示す値の長さや容量を計算するという重要な言語機能の動作を保証しています。

関連リンク

参考にした情報源リンク

  • Go言語のソースコードリポジトリ: https://github.com/golang/go
  • Go言語の初期の設計に関する議論(Goのメーリングリストやデザインドキュメントなど)
    • Goの設計原則に関する情報源を検索しました。特に、lencap の自動間接参照は、Goの「シンプルさ」と「実用性」の哲学に合致するものです。

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

このコミットは、Go言語における組み込み関数 len および cap がポインタ型に対して自動的に間接参照(dereference)を行う機能のテストを追加するものです。具体的には、mapstringarrayslice の各型へのポインタに対して lencap を適用した場合の挙動を検証しています。

コミット

commit cbd08ed2615e9393460760b8d3f2bd352b7e95f5
Author: Russ Cox <rsc@golang.org>
Date:   Fri Jan 9 15:38:01 2009 -0800

    test of automatic indirect
    
    R=r
    DELTA=93  (93 added, 0 deleted, 0 changed)
    OCL=22458
    CL=22461

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

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

元コミット内容

このコミットは、test/indirect.go という新しいテストファイルを追加しています。このファイルは、mapstringarrayslice の各型へのポインタに対して len および cap 関数がどのように振る舞うかを検証するためのものです。特に、これらの関数がポインタを自動的に間接参照して、その指し示す値の長さや容量を返すことを確認しています。また、nil ポインタに対してこれらの関数を呼び出した場合に、コンパイルエラーではなく実行時パニックとなることをも検証しています。

変更の背景

Go言語は、設計当初からシンプルさと実用性を重視していました。lencap のような組み込み関数は、様々なデータ構造のサイズや容量を取得するために頻繁に使用されます。もしこれらの関数がポインタ型に対して直接適用できない場合、開発者は常に明示的な間接参照演算子 * を使用してポインタをデリファレンスする必要がありました。これはコードの冗長性を招き、可読性を低下させる可能性があります。

この「自動間接参照」の導入は、Go言語の設計哲学である「実用性」と「簡潔さ」に合致するものです。これにより、開発者はポインタを介してデータ構造の長さや容量を取得する際に、より自然なコードを書くことができるようになります。このコミットは、この重要な言語機能が正しく動作することを保証するためのテストケースを追加するものです。

前提知識の解説

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

  1. ポインタ (Pointers): Go言語におけるポインタは、変数のメモリアドレスを保持する変数です。*T の形式で宣言され、& 演算子で変数のアドレスを取得し、* 演算子でポインタが指し示す値にアクセス(間接参照)します。 例: var x int = 10; p := &x; fmt.Println(*p)

  2. 組み込み関数 len: len は、Go言語に組み込まれている関数で、様々なデータ構造の「長さ」を返します。

    • 文字列 (string): 文字列のバイト数を返します。
    • 配列 (array): 配列の要素数を返します。
    • スライス (slice): スライスの現在の要素数を返します。
    • マップ (map): マップ内のキーと値のペアの数を返します。
    • チャネル (channel): チャネル内のキューに入れられた要素の数を返します。
  3. 組み込み関数 cap: cap は、Go言語に組み込まれている関数で、スライス、配列、チャネルの「容量」を返します。

    • 配列 (array): 配列の要素数を返します(len と同じ)。
    • スライス (slice): スライスの基底配列の容量を返します。スライスが拡張できる最大要素数を示します。
    • チャネル (channel): チャネルのバッファ容量を返します。
  4. 自動間接参照 (Automatic Dereferencing): Go言語の特定のコンテキスト(特にメソッド呼び出しや一部の組み込み関数)では、ポインタ型に対して操作を行う際に、コンパイラが自動的にポインタを間接参照して、その指し示す値に対して操作を適用します。このコミットは、lencap 関数がこの自動間接参照の恩恵を受けることをテストしています。

  5. nil ポインタ: Go言語では、ポインタが何も指し示していない状態を nil と表現します。nil ポインタを間接参照しようとすると、通常は実行時パニック (runtime panic) が発生します。このテストでは、lencapnil ポインタに対して呼び出された場合に、コンパイルエラーではなくパニックになることを確認しています。これは、型チェックの段階では有効な呼び出しと見なされるが、実行時に値が存在しないためにエラーとなる、というGoの設計思想を反映しています。

技術的詳細

このコミットで追加された test/indirect.go ファイルは、len および cap 関数がポインタ型に対してどのように動作するかを詳細に検証しています。

Go言語のコンパイラは、lencap の引数がポインタ型である場合、そのポインタが指し示す基底の型(mapstringarrayslice)に対してこれらの関数を適用するように自動的に処理します。これは、コンパイラが内部的に len(*p)cap(*p) のように変換していると考えることができます。

テストファイルでは、以下のシナリオが検証されています。

  1. nil ポインタに対する len/cap の呼び出し (crash 関数): var m1 *map[string]int のように nil で初期化されたポインタに対して len(m1) を呼び出しています。コメントには「would crash but should type check」とあり、これはコンパイル時には型エラーとならず、実行時に nil ポインタのデリファレンスによってパニックが発生することを意図しています。これは、Goの型システムが、ポインタが nil でないことを保証するのではなく、その型が正しいことを保証することを示しています。

  2. nil ポインタに対する len/cap の呼び出し (nocrash 関数):

    • map の例: var m2 *map[string]int = &m0 のように、初期化されたマップへのポインタに対して len(m2) を呼び出し、期待される結果(len(m0) と同じ)が得られることを確認しています。
    • string の例: var s4 *string = &s3 のように、初期化された文字列へのポインタに対して len(s4) を呼び出し、期待される結果が得られることを確認しています。
    • array の例: var a2 *[10]int = &a0 のように、初期化された配列へのポインタに対して len(a2) を呼び出し、期待される結果が得られることを確認しています。
    • slice の例: var b4 *[]int = &b3 のように、初期化されたスライスへのポインタに対して len(b4) および cap(b4) を呼び出し、期待される結果が得られることを確認しています。

これらのテストは、lencap がポインタを自動的に間接参照し、その指し示す値の長さや容量を正しく返すというGo言語のセマンティクスを確立し、検証するために不可欠です。

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

このコミットでは、test/indirect.go という新しいファイルが追加されています。

--- /dev/null
+++ b/test/indirect.go
@@ -0,0 +1,97 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG indirect
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+var m0 map[string]int
+var m1 *map[string]int
+var m2 *map[string]int = &m0
+var m3 map[string]int = map[string]int{"a": 1}
+var m4 *map[string]int = &m3
+
+var s0 string
+var s1 *string
+var s2 *string = &s0
+var s3 string = "a"
+var s4 *string = &s3
+
+var a0 [10]int
+var a1 *[10]int
+var a2 *[10]int = &a0
+
+var b0 []int
+var b1 *[]int
+var b2 *[]int = &b0
+var b3 []int = []int{1, 2, 3}
+var b4 *[]int = &b3
+
+func crash()
+{
+	// these uses of nil pointers
+	// would crash but should type check
+	println("crash",
+		len(m1)+
+		len(s1)+
+		len(a1)+
+		len(b1)+
+		cap(b1));
+}
+
+func nocrash()
+{
+	// this is spaced funny so that
+	// the compiler will print a different
+	// line number for each len call if
+	// it decides there are type errors.
+	// it might also help in the traceback.
+	x :=
+		len(m0)+
+		len(m2)+
+		len(m3)+
+		len(m4);
+	if x != 2 {
+		panicln("wrong maplen");
+	}
+
+	x =
+		len(s0)+
+		len(s2)+
+		len(s3)+
+		len(s4);
+	if x != 2 {
+		panicln("wrong stringlen");
+	}
+
+	x =
+		len(a0)+
+		len(a2);
+	if x != 20 {
+		panicln("wrong arraylen");
+	}
+
+	x =
+		len(b0)+
+		len(b2)+
+		len(b3)+
+		len(b4);
+	if x != 6 {
+		panicln("wrong slicelen");
+	}
+
+	x =
+		cap(b0)+
+		cap(b2)+
+		cap(b3)+
+		cap(b4);
+	if x != 6 {
+		panicln("wrong slicecap\");
+	}
+}
+
+func main()
+{
+	nocrash();
+}

コアとなるコードの解説

test/indirect.go は、Go言語のテストフレームワークを使用せず、直接コンパイルと実行を行うシンプルなテストスクリプトです。

  1. 変数宣言: m0 から b4 までのグローバル変数が宣言されています。これらは、mapstringarrayslice の各型、およびそれらへのポインタ型を含んでいます。特に、m1, s1, a1, b1nil ポインタとして初期化されており、m2, s2, a2, b2, b4 はそれぞれ対応する非ポインタ変数へのポインタとして初期化されています。

  2. crash() 関数: この関数は、nil ポインタに対して lencap を呼び出すことを意図しています。 println ステートメント内で len(m1)len(s1)len(a1)len(b1)cap(b1) を呼び出しています。 コメントにあるように、これらの呼び出しはコンパイル時には型チェックを通過しますが、実行時には nil ポインタのデリファレンスによってパニックを引き起こすはずです。この関数は main から直接呼び出されていませんが、テストの意図を明確にするために存在しています。

  3. nocrash() 関数: この関数は、非nil の変数およびポインタに対して lencap を呼び出し、その結果が期待通りであることを検証します。

    • map の長さの検証: len(m0) (初期化されていないマップは長さ0)、len(m2) (m0へのポインタ)、len(m3) (要素が1つのマップ)、len(m4) (m3へのポインタ) の合計が 0 + 0 + 1 + 1 = 2 になることを確認しています。
    • string の長さの検証: len(s0) (空文字列は長さ0)、len(s2) (s0へのポインタ)、len(s3) (文字列"a"は長さ1)、len(s4) (s3へのポインタ) の合計が 0 + 0 + 1 + 1 = 2 になることを確認しています。
    • array の長さの検証: len(a0) (要素数10の配列)、len(a2) (a0へのポインタ) の合計が 10 + 10 = 20 になることを確認しています。
    • slice の長さの検証: len(b0) (nilスライスは長さ0)、len(b2) (b0へのポインタ)、len(b3) (要素数3のスライス)、len(b4) (b3へのポインタ) の合計が 0 + 0 + 3 + 3 = 6 になることを確認しています。
    • slice の容量の検証: cap(b0) (nilスライスは容量0)、cap(b2) (b0へのポインタ)、cap(b3) (要素数3のスライスは容量3)、cap(b4) (b3へのポインタ) の合計が 0 + 0 + 3 + 3 = 6 になることを確認しています。 各検証の後には panicln を用いて、期待される値と異なる場合にテストが失敗することを示しています。
  4. main() 関数: nocrash() 関数のみを呼び出しています。これにより、nil ポインタのテストケース (crash() 関数) は実行されず、自動間接参照が正しく機能する場合のテストのみが実行されます。

このテストファイルは、Go言語のコンパイラが lencap の引数としてポインタを受け取った際に、自動的にそのポインタを間接参照して、指し示す値の長さや容量を計算するという重要な言語機能の動作を保証しています。

関連リンク

参考にした情報源リンク

  • Go言語のソースコードリポジトリ: https://github.com/golang/go
  • Go言語の設計原則に関する一般的な情報源(Goの「シンプルさ」と「実用性」の哲学は、このような自動間接参照の導入の背景となっています)。