[インデックス 1458] ファイルの概要
このコミットは、Go言語における組み込み関数 len
および cap
がポインタ型に対して自動的に間接参照(dereference)を行う機能のテストを追加するものです。具体的には、map
、string
、array
、slice
の各型へのポインタに対して len
や cap
を適用した場合の挙動を検証しています。
コミット
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
という新しいテストファイルを追加しています。このファイルは、map
、string
、array
、slice
の各型へのポインタに対して len
および cap
関数がどのように振る舞うかを検証するためのものです。特に、これらの関数がポインタを自動的に間接参照して、その指し示す値の長さや容量を返すことを確認しています。また、nil
ポインタに対してこれらの関数を呼び出した場合に、コンパイルエラーではなく実行時パニックとなることをも検証しています。
変更の背景
Go言語は、設計当初からシンプルさと実用性を重視していました。len
や cap
のような組み込み関数は、様々なデータ構造のサイズや容量を取得するために頻繁に使用されます。もしこれらの関数がポインタ型に対して直接適用できない場合、開発者は常に明示的な間接参照演算子 *
を使用してポインタをデリファレンスする必要がありました。これはコードの冗長性を招き、可読性を低下させる可能性があります。
この「自動間接参照」の導入は、Go言語の設計哲学である「実用性」と「簡潔さ」に合致するものです。これにより、開発者はポインタを介してデータ構造の長さや容量を取得する際に、より自然なコードを書くことができるようになります。このコミットは、この重要な言語機能が正しく動作することを保証するためのテストケースを追加するものです。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的な概念を理解しておく必要があります。
-
ポインタ (Pointers): Go言語におけるポインタは、変数のメモリアドレスを保持する変数です。
*T
の形式で宣言され、&
演算子で変数のアドレスを取得し、*
演算子でポインタが指し示す値にアクセス(間接参照)します。 例:var x int = 10; p := &x; fmt.Println(*p)
-
組み込み関数
len
:len
は、Go言語に組み込まれている関数で、様々なデータ構造の「長さ」を返します。- 文字列 (string): 文字列のバイト数を返します。
- 配列 (array): 配列の要素数を返します。
- スライス (slice): スライスの現在の要素数を返します。
- マップ (map): マップ内のキーと値のペアの数を返します。
- チャネル (channel): チャネル内のキューに入れられた要素の数を返します。
-
組み込み関数
cap
:cap
は、Go言語に組み込まれている関数で、スライス、配列、チャネルの「容量」を返します。- 配列 (array): 配列の要素数を返します(
len
と同じ)。 - スライス (slice): スライスの基底配列の容量を返します。スライスが拡張できる最大要素数を示します。
- チャネル (channel): チャネルのバッファ容量を返します。
- 配列 (array): 配列の要素数を返します(
-
自動間接参照 (Automatic Dereferencing): Go言語の特定のコンテキスト(特にメソッド呼び出しや一部の組み込み関数)では、ポインタ型に対して操作を行う際に、コンパイラが自動的にポインタを間接参照して、その指し示す値に対して操作を適用します。このコミットは、
len
とcap
関数がこの自動間接参照の恩恵を受けることをテストしています。 -
nil
ポインタ: Go言語では、ポインタが何も指し示していない状態をnil
と表現します。nil
ポインタを間接参照しようとすると、通常は実行時パニック (runtime panic) が発生します。このテストでは、len
やcap
がnil
ポインタに対して呼び出された場合に、コンパイルエラーではなくパニックになることを確認しています。これは、型チェックの段階では有効な呼び出しと見なされるが、実行時に値が存在しないためにエラーとなる、というGoの設計思想を反映しています。
技術的詳細
このコミットで追加された test/indirect.go
ファイルは、len
および cap
関数がポインタ型に対してどのように動作するかを詳細に検証しています。
Go言語のコンパイラは、len
や cap
の引数がポインタ型である場合、そのポインタが指し示す基底の型(map
、string
、array
、slice
)に対してこれらの関数を適用するように自動的に処理します。これは、コンパイラが内部的に len(*p)
や cap(*p)
のように変換していると考えることができます。
テストファイルでは、以下のシナリオが検証されています。
-
nil
ポインタに対するlen
/cap
の呼び出し (crash
関数):var m1 *map[string]int
のようにnil
で初期化されたポインタに対してlen(m1)
を呼び出しています。コメントには「would crash but should type check」とあり、これはコンパイル時には型エラーとならず、実行時にnil
ポインタのデリファレンスによってパニックが発生することを意図しています。これは、Goの型システムが、ポインタがnil
でないことを保証するのではなく、その型が正しいことを保証することを示しています。 -
非
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)
を呼び出し、期待される結果が得られることを確認しています。
これらのテストは、len
と cap
がポインタを自動的に間接参照し、その指し示す値の長さや容量を正しく返すという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言語のテストフレームワークを使用せず、直接コンパイルと実行を行うシンプルなテストスクリプトです。
-
変数宣言:
m0
からb4
までのグローバル変数が宣言されています。これらは、map
、string
、array
、slice
の各型、およびそれらへのポインタ型を含んでいます。特に、m1
,s1
,a1
,b1
はnil
ポインタとして初期化されており、m2
,s2
,a2
,b2
,b4
はそれぞれ対応する非ポインタ変数へのポインタとして初期化されています。 -
crash()
関数: この関数は、nil
ポインタに対してlen
やcap
を呼び出すことを意図しています。println
ステートメント内でlen(m1)
、len(s1)
、len(a1)
、len(b1)
、cap(b1)
を呼び出しています。 コメントにあるように、これらの呼び出しはコンパイル時には型チェックを通過しますが、実行時にはnil
ポインタのデリファレンスによってパニックを引き起こすはずです。この関数はmain
から直接呼び出されていませんが、テストの意図を明確にするために存在しています。 -
nocrash()
関数: この関数は、非nil
の変数およびポインタに対してlen
やcap
を呼び出し、その結果が期待通りであることを検証します。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
を用いて、期待される値と異なる場合にテストが失敗することを示しています。
-
main()
関数:nocrash()
関数のみを呼び出しています。これにより、nil
ポインタのテストケース (crash()
関数) は実行されず、自動間接参照が正しく機能する場合のテストのみが実行されます。
このテストファイルは、Go言語のコンパイラが len
や cap
の引数としてポインタを受け取った際に、自動的にそのポインタを間接参照して、指し示す値の長さや容量を計算するという重要な言語機能の動作を保証しています。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語の
len
およびcap
組み込み関数に関するドキュメント: https://go.dev/ref/spec#Length_and_capacity
参考にした情報源リンク
- Go言語のソースコードリポジトリ: https://github.com/golang/go
- Go言語の初期の設計に関する議論(Goのメーリングリストやデザインドキュメントなど)
- Goの設計原則に関する情報源を検索しました。特に、
len
やcap
の自動間接参照は、Goの「シンプルさ」と「実用性」の哲学に合致するものです。
- Goの設計原則に関する情報源を検索しました。特に、
[インデックス 1458] ファイルの概要
このコミットは、Go言語における組み込み関数 len
および cap
がポインタ型に対して自動的に間接参照(dereference)を行う機能のテストを追加するものです。具体的には、map
、string
、array
、slice
の各型へのポインタに対して len
や cap
を適用した場合の挙動を検証しています。
コミット
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
という新しいテストファイルを追加しています。このファイルは、map
、string
、array
、slice
の各型へのポインタに対して len
および cap
関数がどのように振る舞うかを検証するためのものです。特に、これらの関数がポインタを自動的に間接参照して、その指し示す値の長さや容量を返すことを確認しています。また、nil
ポインタに対してこれらの関数を呼び出した場合に、コンパイルエラーではなく実行時パニックとなることをも検証しています。
変更の背景
Go言語は、設計当初からシンプルさと実用性を重視していました。len
や cap
のような組み込み関数は、様々なデータ構造のサイズや容量を取得するために頻繁に使用されます。もしこれらの関数がポインタ型に対して直接適用できない場合、開発者は常に明示的な間接参照演算子 *
を使用してポインタをデリファレンスする必要がありました。これはコードの冗長性を招き、可読性を低下させる可能性があります。
この「自動間接参照」の導入は、Go言語の設計哲学である「実用性」と「簡潔さ」に合致するものです。これにより、開発者はポインタを介してデータ構造の長さや容量を取得する際に、より自然なコードを書くことができるようになります。このコミットは、この重要な言語機能が正しく動作することを保証するためのテストケースを追加するものです。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的な概念を理解しておく必要があります。
-
ポインタ (Pointers): Go言語におけるポインタは、変数のメモリアドレスを保持する変数です。
*T
の形式で宣言され、&
演算子で変数のアドレスを取得し、*
演算子でポインタが指し示す値にアクセス(間接参照)します。 例:var x int = 10; p := &x; fmt.Println(*p)
-
組み込み関数
len
:len
は、Go言語に組み込まれている関数で、様々なデータ構造の「長さ」を返します。- 文字列 (string): 文字列のバイト数を返します。
- 配列 (array): 配列の要素数を返します。
- スライス (slice): スライスの現在の要素数を返します。
- マップ (map): マップ内のキーと値のペアの数を返します。
- チャネル (channel): チャネル内のキューに入れられた要素の数を返します。
-
組み込み関数
cap
:cap
は、Go言語に組み込まれている関数で、スライス、配列、チャネルの「容量」を返します。- 配列 (array): 配列の要素数を返します(
len
と同じ)。 - スライス (slice): スライスの基底配列の容量を返します。スライスが拡張できる最大要素数を示します。
- チャネル (channel): チャネルのバッファ容量を返します。
- 配列 (array): 配列の要素数を返します(
-
自動間接参照 (Automatic Dereferencing): Go言語の特定のコンテキスト(特にメソッド呼び出しや一部の組み込み関数)では、ポインタ型に対して操作を行う際に、コンパイラが自動的にポインタを間接参照して、その指し示す値に対して操作を適用します。このコミットは、
len
とcap
関数がこの自動間接参照の恩恵を受けることをテストしています。 -
nil
ポインタ: Go言語では、ポインタが何も指し示していない状態をnil
と表現します。nil
ポインタを間接参照しようとすると、通常は実行時パニック (runtime panic) が発生します。このテストでは、len
やcap
がnil
ポインタに対して呼び出された場合に、コンパイルエラーではなくパニックになることを確認しています。これは、型チェックの段階では有効な呼び出しと見なされるが、実行時に値が存在しないためにエラーとなる、というGoの設計思想を反映しています。
技術的詳細
このコミットで追加された test/indirect.go
ファイルは、len
および cap
関数がポインタ型に対してどのように動作するかを詳細に検証しています。
Go言語のコンパイラは、len
や cap
の引数がポインタ型である場合、そのポインタが指し示す基底の型(map
、string
、array
、slice
)に対してこれらの関数を適用するように自動的に処理します。これは、コンパイラが内部的に len(*p)
や cap(*p)
のように変換していると考えることができます。
テストファイルでは、以下のシナリオが検証されています。
-
nil
ポインタに対するlen
/cap
の呼び出し (crash
関数):var m1 *map[string]int
のようにnil
で初期化されたポインタに対してlen(m1)
を呼び出しています。コメントには「would crash but should type check」とあり、これはコンパイル時には型エラーとならず、実行時にnil
ポインタのデリファレンスによってパニックが発生することを意図しています。これは、Goの型システムが、ポインタがnil
でないことを保証するのではなく、その型が正しいことを保証することを示しています。 -
非
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)
を呼び出し、期待される結果が得られることを確認しています。
これらのテストは、len
と cap
がポインタを自動的に間接参照し、その指し示す値の長さや容量を正しく返すという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言語のテストフレームワークを使用せず、直接コンパイルと実行を行うシンプルなテストスクリプトです。
-
変数宣言:
m0
からb4
までのグローバル変数が宣言されています。これらは、map
、string
、array
、slice
の各型、およびそれらへのポインタ型を含んでいます。特に、m1
,s1
,a1
,b1
はnil
ポインタとして初期化されており、m2
,s2
,a2
,b2
,b4
はそれぞれ対応する非ポインタ変数へのポインタとして初期化されています。 -
crash()
関数: この関数は、nil
ポインタに対してlen
やcap
を呼び出すことを意図しています。println
ステートメント内でlen(m1)
、len(s1)
、len(a1)
、len(b1)
、cap(b1)
を呼び出しています。 コメントにあるように、これらの呼び出しはコンパイル時には型チェックを通過しますが、実行時にはnil
ポインタのデリファレンスによってパニックを引き起こすはずです。この関数はmain
から直接呼び出されていませんが、テストの意図を明確にするために存在しています。 -
nocrash()
関数: この関数は、非nil
の変数およびポインタに対してlen
やcap
を呼び出し、その結果が期待通りであることを検証します。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
を用いて、期待される値と異なる場合にテストが失敗することを示しています。
-
main()
関数:nocrash()
関数のみを呼び出しています。これにより、nil
ポインタのテストケース (crash()
関数) は実行されず、自動間接参照が正しく機能する場合のテストのみが実行されます。
このテストファイルは、Go言語のコンパイラが len
や cap
の引数としてポインタを受け取った際に、自動的にそのポインタを間接参照して、指し示す値の長さや容量を計算するという重要な言語機能の動作を保証しています。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語の
len
およびcap
組み込み関数に関するドキュメント: https://go.dev/ref/spec#Length_and_capacity
参考にした情報源リンク
- Go言語のソースコードリポジトリ: https://github.com/golang/go
- Go言語の設計原則に関する一般的な情報源(Goの「シンプルさ」と「実用性」の哲学は、このような自動間接参照の導入の背景となっています)。