[インデックス 1667] ファイルの概要
このコミットは、Go言語の初期段階における言語仕様の重要な変更を反映しています。主に、複合リテラルの構文変更、配列連結演算子の廃止、およびunsafe
パッケージに関するドキュメントの更新が含まれています。これらの変更は、Go言語の設計思想と実用性を向上させるために行われました。
コミット
commit 6f8df7aa3e8cb6251d4aec424b395ebc686353c7
Author: Robert Griesemer <gri@golang.org>
Date: Wed Feb 11 21:57:15 2009 -0800
- syntax for composite literals use () instead of {}
- do not permit + for array concatenation anymore
(not implemented and not a good idea)
- document that unsafe function results are compile time constants
- fixed minor typos
DELTA=41 (7 added, 11 deleted, 23 changed)
OCL=24899
CL=24927
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6f8df7aa3e8cb6251d4aec424b395ebc686353c7
元コミット内容
このコミットは、Go言語の仕様書であるdoc/go_spec.txt
に対して以下の変更を加えています。
- 複合リテラルの構文変更: 複合リテラル(配列、スライス、構造体、マップの初期化)の構文が、波括弧
{}
から丸括弧()
に変更されました。 - 配列連結演算子の廃止: 配列の連結に
+
演算子を使用することが許可されなくなりました。これは元々実装されておらず、設計上も好ましくないと判断されました。 unsafe
パッケージのドキュメント更新:unsafe.Alignof
,unsafe.Offsetof
,unsafe.Sizeof
の結果がコンパイル時定数であるという記述が追加されました。- 軽微なタイプミス修正: ドキュメント内のいくつかのタイプミスが修正されました。
- 著者リストの更新: 仕様書の著者リストにRuss Coxが追加されました。
変更の背景
このコミットが行われた2009年2月は、Go言語がまだ活発に開発され、その仕様が固まりつつあった非常に初期の段階です。言語設計者は、構文の明確性、一貫性、および将来的な拡張性を考慮して、様々な試行錯誤を行っていました。
-
複合リテラルの構文変更の背景: 初期のGo言語では、複合リテラルに波括弧
{}
を使用していました。しかし、これはif
文などの他の構文との間で解析の曖昧さを生じさせる可能性がありました。例えば、T{...}
のような構文が、型T
の複合リテラルなのか、あるいはif
文のブロックの一部なのかをコンパイラが区別するのが難しいケースが考えられました。この曖昧さを解消し、パーサーの複雑さを軽減するために、複合リテラルに丸括弧()
を使用する構文に変更されました。これは、Go言語の設計哲学である「シンプルさ」と「明確さ」を追求した結果と言えます。 -
配列連結演算子廃止の背景: 配列の連結に
+
演算子を使用するというアイデアは、Go言語の初期の設計案には存在しましたが、実際に実装されることはありませんでした。配列は固定長であり、連結操作は新しい配列を生成し、要素をコピーする必要があるため、パフォーマンス上のオーバーヘッドが大きくなります。また、Go言語は明示的な操作を好む傾向があり、暗黙的なコピーを伴う+
演算子による配列連結は、その哲学に反すると判断された可能性があります。スライスが導入されたことで、より柔軟で効率的な動的配列操作が可能になり、固定長配列の連結演算子の必要性はさらに低下しました。 -
unsafe
パッケージのドキュメント更新の背景:unsafe
パッケージは、Go言語の型安全性をバイパスして低レベルな操作を可能にするためのものです。このパッケージの関数(Alignof
,Offsetof
,Sizeof
)は、メモリレイアウトに関する情報を提供します。これらの情報がコンパイル時定数であるという明確な記述は、コンパイラがこれらの値を最適化に利用できること、およびプログラムの実行時にこれらの値が変化しないことを保証するために重要です。これは、Go言語のパフォーマンス特性と予測可能性を向上させるためのドキュメント上の明確化です。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と初期の設計に関する知識が役立ちます。
-
複合リテラル (Composite Literals): Go言語において、配列、スライス、構造体、マップなどの複合型を初期化するための構文です。例えば、
[]int{1, 2, 3}
はスライスリテラル、Point{X: 10, Y: 20}
は構造体リテラルです。これらは、宣言と同時に値を割り当てるための簡潔な方法を提供します。 -
配列とスライス (Arrays and Slices):
- 配列 (Array): 固定長の要素のシーケンスです。宣言時に長さが決定され、実行中に変更することはできません。例:
[3]int{1, 2, 3}
。 - スライス (Slice): 可変長のシーケンスであり、配列の上に構築されます。スライスは、基になる配列の一部を参照します。Go言語で動的なリストを扱う際の主要なデータ構造です。例:
[]int{1, 2, 3}
。
- 配列 (Array): 固定長の要素のシーケンスです。宣言時に長さが決定され、実行中に変更することはできません。例:
-
unsafe
パッケージ: Go言語の標準ライブラリの一部ですが、その名の通り「安全ではない」操作を可能にするためのパッケージです。主に、ポインタと整数間の変換、任意の型のポインタへの変換、メモリのアライメントやサイズの取得など、低レベルなメモリ操作を行うために使用されます。unsafe
パッケージを使用するコードは、Goの型システムによる安全性の保証を受けられないため、非常に注意深く記述する必要があります。 -
コンパイル時定数 (Compile-time Constants): プログラムのコンパイル時に値が決定され、実行時には変更されない定数のことです。コンパイル時定数であることは、コンパイラがコードを最適化する上で重要な情報となります。
-
Go言語の設計哲学: Go言語は、シンプルさ、効率性、並行処理の容易さを重視して設計されました。構文は簡潔で、曖昧さを排除し、コンパイラの処理を高速化することを目指しています。
技術的詳細
複合リテラルの構文変更 ({}
から ()
へ)
この変更は、Go言語のパーサーの設計における重要な決定でした。初期のGo言語では、複合リテラルに波括弧 {}
を使用していました。例えば、構造体の初期化はPoint{X: 1, Y: 2}
のようになります。しかし、この構文は特定の文脈で曖昧さを生じさせました。
具体的には、Go言語のif
文やfor
文のブロックも波括弧 {}
を使用します。もし、型名が変数名や関数名と衝突した場合、コンパイラはT{...}
が型T
の複合リテラルなのか、あるいはT
という名前の変数や関数に関連するブロックなのかを区別するのが難しくなる可能性がありました。
例えば、以下のような状況を考えます(これはあくまで概念的な例であり、実際のGo言語のパーサーがどのように動作するかを厳密に反映しているわけではありませんが、曖昧さの概念を説明するものです)。
// もしTが型名でもあり、かつ変数名や関数名としても使われた場合
T {
// ここが複合リテラルの内容なのか、それともTに関連するコードブロックなのか?
}
このような曖昧さを避けるため、Go言語の設計者は複合リテラルの構文を丸括弧 ()
に変更することを決定しました。これにより、T(...)
という構文は明確に型T
の複合リテラルを示し、他の構文との衝突を避けることができます。この変更は、Go言語のパーサーをよりシンプルかつ堅牢にする上で不可欠でした。
配列連結演算子の廃止
Go言語の初期の設計では、文字列と同様に配列も +
演算子で連結できる可能性が検討されていました。しかし、この機能は最終的に実装されず、このコミットで仕様から削除されました。
配列は固定長であるため、a + b
のような配列連結は、a
とb
の要素をすべて含む新しい配列を生成し、そこに要素をコピーする必要があります。これは、特に大きな配列の場合、メモリ割り当てとコピーのオーバーヘッドが大きくなります。Go言語はパフォーマンスを重視しており、このような暗黙的な高コスト操作は避ける傾向があります。
また、Go言語にはスライスという、より柔軟で効率的な動的配列の概念があります。スライスを使用すれば、append
関数などを用いて要素を追加したり、複数のスライスを結合したりすることが可能です。スライスは基になる配列を参照するため、不要なコピーを避けることができます。配列の連結演算子を廃止し、スライスとappend
関数に一本化することで、言語の複雑さを減らし、よりGoらしいイディオムを促進しました。
unsafe
パッケージ関数のコンパイル時定数化
unsafe
パッケージのAlignof
, Offsetof
, Sizeof
関数は、Goの型システムをバイパスして、特定の型のメモリレイアウトに関する情報(アライメント、フィールドのオフセット、サイズ)をバイト単位で返します。
このコミットで、これらの関数の結果が「コンパイル時定数」であると明記されました。これは以下の意味を持ちます。
- 最適化: コンパイラは、これらの関数の呼び出しをコンパイル時に計算し、その結果を直接コードに埋め込むことができます。これにより、実行時の計算オーバーヘッドがなくなります。
- 予測可能性: これらの値は、プログラムの実行環境や実行時の状態に依存せず、常に同じ値を取ることが保証されます。これは、低レベルなシステムプログラミングや、特定のメモリレイアウトに依存するコードを書く際に非常に重要です。
- 型安全性とのバランス:
unsafe
パッケージは型安全性を犠牲にしますが、その結果がコンパイル時定数であるという保証は、少なくともこれらの情報が実行時に不確定な要素とならないことを意味し、ある程度の予測可能性と制御を提供します。
コアとなるコードの変更箇所
このコミットの主要な変更は、doc/go_spec.txt
ファイル内の以下の部分に集中しています。
-
複合リテラルの文法定義の変更:
--- a/doc/go_spec.txt +++ b/doc/go_spec.txt @@ -1820,7 +1821,7 @@ Literals for composite data structures consist of the type of the value followed by a braced expression list for array, slice, and structure literals, or a list of expression pairs for map literals. - CompositeLit = LiteralType "{" [ ( ExpressionList | ExprPairList ) [ "," ] ] "}" . + CompositeLit = LiteralType "(" [ ( ExpressionList | ExprPairList ) [ "," ] ] ")" . LiteralType = Type | "[" "..." "]" ElementType . ExprPairList = ExprPair { "," ExprPair } . ExprPair = Expression ":" Expression .
CompositeLit
の定義で、波括弧{}
が丸括弧()
に変更されています。 -
複合リテラルの使用例の更新:
--- a/doc/go_spec.txt +++ b/doc/go_spec.txt @@ -1839,7 +1840,7 @@ Given one can write - pi := Num{Rat{22, 7}, 3.14159, "pi"}; + pi := Num(Rat(22, 7), 3.14159, "pi"); The length of an array literal is the length specified in the LiteralType. If fewer elements than the length are provided in the literal, the missing @@ -1848,24 +1849,24 @@ It is an error to provide more elements than specified in LiteralType. The notation "..." may be used in place of the length expression to denote a length equal to the number of elements in the literal. - buffer := [10]string{}; // len(buffer) == 10 - primes := [6]int{2, 3, 5, 7, 9, 11}; // len(primes) == 6 - days := [...]string{"sat", "sun"}; // len(days) == 2 + buffer := [10]string(); // len(buffer) == 10 + primes := [6]int(2, 3, 5, 7, 9, 11); // len(primes) == 6 + days := [...]string("sat", "sun"); // len(days) == 2 A slice literal is a slice describing the entire underlying array literal. Thus, the length and capacity of a slice literal is the number of elements provided in the literal. A slice literal of the form - []T{x1, x2, ... xn}\n + []T(x1, x2, ... xn)\n is essentially a shortcut for a slice operation applied to an array literal: - [n]T{x1, x2, ... xn}[0 : n]\n + [n]T(x1, x2, ... xn)[0 : n]\n Map literals are similar except the elements of the expression list are key-value pairs separated by a colon: - m := map[string]int{"good": 0, "bad": 1, "indifferent": 7}; + m := map[string]int("good": 0, "bad": 1, "indifferent": 7);
複合リテラルの様々な例が、波括弧
{}
から丸括弧()
を使用するように変更されています。 -
配列連結に関する記述の削除:
--- a/doc/go_spec.txt +++ b/doc/go_spec.txt @@ -2282,14 +2283,11 @@ to strings and arrays; all other arithmetic operators apply to integer types onl << left shift integer << unsigned integer >> right shift integer >> unsigned integer -Strings and arrays can be concatenated using the "+" operator -(or via the "+=" assignment): +Strings can be concatenated using the "+" operator (or the "+=" assignment): s := "hi" + string(c) - a += []int{5, 6, 7} -String and array addition creates a new array or string by copying the -elements. +String addition creates a new string by copying the elements. For integer values, "/" and "%" satisfy the following relationship:
配列の連結に関する記述と例 (
a += []int{5, 6, 7}
) が削除されています。 -
unsafe
パッケージに関する記述の追加:--- a/doc/go_spec.txt +++ b/doc/go_spec.txt @@ -3532,6 +3525,9 @@ a variable "x" of the largest arithmetic type (8 for a float64), but may be smaller on systems that have less stringent alignment restrictions or are space constrained. +The results of calls to "unsafe.Alignof", "unsafe.Offsetof", and +"unsafe.Sizeof" are compile-time constants.\n + Size and alignment guarantees ----
unsafe.Alignof
,unsafe.Offsetof
,unsafe.Sizeof
の結果がコンパイル時定数であるという記述が追加されています。
コアとなるコードの解説
このコミットは、Go言語の仕様書であるdoc/go_spec.txt
に対する変更であり、実際のGoコンパイラやランタイムのコード変更ではありません。しかし、仕様書の変更は、Go言語の構文とセマンティクスがどのように定義され、将来のコンパイラ実装にどのように影響するかを直接示しています。
-
複合リテラルの構文変更:
CompositeLit = LiteralType "{" ... "}"
からCompositeLit = LiteralType "(" ... ")"
への変更は、Go言語のパーサーが複合リテラルを認識する方法を根本的に変えることを意味します。これにより、パーサーは波括弧{}
を見たときに、それがコードブロックなのか複合リテラルなのかを判断する際の曖昧さが解消され、よりシンプルで効率的な解析が可能になります。これは、言語の設計段階で構文の明確性を追求した結果です。 -
配列連結演算子の廃止: 配列の連結に
+
演算子を使用する記述が削除されたことは、Go言語が配列の連結にこの演算子をサポートしないことを明確にしています。これは、Go言語が固定長配列の操作を明示的に行わせる方針であり、暗黙的なコピーを伴う操作を避けるという設計思想を反映しています。代わりに、スライスとappend
関数が推奨されるイディオムとなります。 -
unsafe
パッケージのドキュメント更新:unsafe
パッケージの関数が返す値がコンパイル時定数であるという記述は、これらの関数がGoプログラムの実行時に動的にメモリレイアウトを計算するのではなく、コンパイル時に静的に決定されることを保証します。これは、Go言語のコンパイラがこれらの情報を利用して、より効率的な機械語コードを生成できることを示唆しています。例えば、構造体のフィールドへのアクセスを最適化する際に、オフセットがコンパイル時に既知であれば、実行時の計算を省略できます。
これらの変更は、Go言語がまだ初期段階にあった頃に、言語の堅牢性、パフォーマンス、そして開発者の使いやすさを向上させるために行われた重要な設計上の決定を反映しています。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語の仕様書: https://go.dev/ref/spec (現在の最新版)
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語の初期の設計に関する議論 (Go Mailing List archivesなど、一般には公開されていない可能性が高いが、当時の設計決定の背景を理解する上で重要)
- Go言語のブログ (初期の言語変更に関する記事がある可能性): https://go.dev/blog/
- Go言語の
unsafe
パッケージに関するドキュメント: https://pkg.go.dev/unsafe - Go言語の複合リテラルに関するドキュメント: https://go.dev/ref/spec#Composite_literals
- Go言語の配列とスライスに関するドキュメント: https://go.dev/blog/slices-intro
- Go言語の歴史に関する記事や講演 (例: "The Evolution of Go" by Robert Griesemer, "Go at Google: Language Design in the Service of Software Engineering" by Rob Pike)
これらの情報は、Go言語の設計思想と、なぜ特定の設計決定がなされたのかを深く理解するのに役立ちます。# [インデックス 1667] ファイルの概要
このコミットは、Go言語の初期段階における言語仕様の重要な変更を反映しています。主に、複合リテラルの構文変更、配列連結演算子の廃止、およびunsafe
パッケージに関するドキュメントの更新が含まれています。これらの変更は、Go言語の設計思想と実用性を向上させるために行われました。
コミット
commit 6f8df7aa3e8cb6251d4aec424b395ebc686353c7
Author: Robert Griesemer <gri@golang.org>
Date: Wed Feb 11 21:57:15 2009 -0800
- syntax for composite literals use () instead of {}
- do not permit + for array concatenation anymore
(not implemented and not a good idea)
- document that unsafe function results are compile time constants
- fixed minor typos
DELTA=41 (7 added, 11 deleted, 23 changed)
OCL=24899
CL=24927
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6f8df7aa3e8cb6251d4aec424b395ebc686353c7
元コミット内容
このコミットは、Go言語の仕様書であるdoc/go_spec.txt
に対して以下の変更を加えています。
- 複合リテラルの構文変更: 複合リテラル(配列、スライス、構造体、マップの初期化)の構文が、波括弧
{}
から丸括弧()
に変更されました。 - 配列連結演算子の廃止: 配列の連結に
+
演算子を使用することが許可されなくなりました。これは元々実装されておらず、設計上も好ましくないと判断されました。 unsafe
パッケージのドキュメント更新:unsafe.Alignof
,unsafe.Offsetof
,unsafe.Sizeof
の結果がコンパイル時定数であるという記述が追加されました。- 軽微なタイプミス修正: ドキュメント内のいくつかのタイプミスが修正されました。
- 著者リストの更新: 仕様書の著者リストにRuss Coxが追加されました。
変更の背景
このコミットが行われた2009年2月は、Go言語がまだ活発に開発され、その仕様が固まりつつあった非常に初期の段階です。言語設計者は、構文の明確性、一貫性、および将来的な拡張性を考慮して、様々な試行錯誤を行っていました。
-
複合リテラルの構文変更の背景: 初期のGo言語では、複合リテラルに波括弧
{}
を使用していました。しかし、これはif
文などの他の構文との間で解析の曖昧さを生じさせる可能性がありました。例えば、T{...}
のような構文が、型T
の複合リテラルなのか、あるいはif
文のブロックの一部なのかをコンパイラが区別するのが難しいケースが考えられました。この曖昧さを解消し、パーサーの複雑さを軽減するために、複合リテラルに丸括弧()
を使用する構文に変更されました。これは、Go言語の設計哲学である「シンプルさ」と「明確さ」を追求した結果と言えます。 -
配列連結演算子廃止の背景: 配列の連結に
+
演算子を使用するというアイデアは、Go言語の初期の設計案には存在しましたが、実際に実装されることはありませんでした。配列は固定長であり、連結操作は新しい配列を生成し、要素をコピーする必要があるため、パフォーマンス上のオーバーヘッドが大きくなります。また、Go言語は明示的な操作を好む傾向があり、暗黙的なコピーを伴う+
演算子による配列連結は、その哲学に反すると判断された可能性があります。スライスが導入されたことで、より柔軟で効率的な動的配列操作が可能になり、固定長配列の連結演算子の必要性はさらに低下しました。 -
unsafe
パッケージのドキュメント更新の背景:unsafe
パッケージは、Go言語の型安全性をバイパスして低レベルな操作を可能にするためのものです。このパッケージの関数(Alignof
,Offsetof
,Sizeof
)は、メモリレイアウトに関する情報を提供します。これらの情報がコンパイル時定数であるという明確な記述は、コンパイラがこれらの値を最適化に利用できること、およびプログラムの実行時にこれらの値が変化しないことを保証するために重要です。これは、Go言語のパフォーマンス特性と予測可能性を向上させるためのドキュメント上の明確化です。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と初期の設計に関する知識が役立ちます。
-
複合リテラル (Composite Literals): Go言語において、配列、スライス、構造体、マップなどの複合型を初期化するための構文です。例えば、
[]int{1, 2, 3}
はスライスリテラル、Point{X: 10, Y: 20}
は構造体リテラルです。これらは、宣言と同時に値を割り当てるための簡潔な方法を提供します。 -
配列とスライス (Arrays and Slices):
- 配列 (Array): 固定長の要素のシーケンスです。宣言時に長さが決定され、実行中に変更することはできません。例:
[3]int{1, 2, 3}
。 - スライス (Slice): 可変長のシーケンスであり、配列の上に構築されます。スライスは、基になる配列の一部を参照します。Go言語で動的なリストを扱う際の主要なデータ構造です。例:
[]int{1, 2, 3}
。
- 配列 (Array): 固定長の要素のシーケンスです。宣言時に長さが決定され、実行中に変更することはできません。例:
-
unsafe
パッケージ: Go言語の標準ライブラリの一部ですが、その名の通り「安全ではない」操作を可能にするためのパッケージです。主に、ポインタと整数間の変換、任意の型のポインタへの変換、メモリのアライメントやサイズの取得など、低レベルなメモリ操作を行うために使用されます。unsafe
パッケージを使用するコードは、Goの型システムによる安全性の保証を受けられないため、非常に注意深く記述する必要があります。 -
コンパイル時定数 (Compile-time Constants): プログラムのコンパイル時に値が決定され、実行時には変更されない定数のことです。コンパイル時定数であることは、コンパイラがコードを最適化する上で重要な情報となります。
-
Go言語の設計哲学: Go言語は、シンプルさ、効率性、並行処理の容易さを重視して設計されました。構文は簡潔で、曖昧さを排除し、コンパイラの処理を高速化することを目指しています。
技術的詳細
複合リテラルの構文変更 ({}
から ()
へ)
この変更は、Go言語のパーサーの設計における重要な決定でした。初期のGo言語では、複合リテラルに波括弧 {}
を使用していました。例えば、構造体の初期化はPoint{X: 1, Y: 2}
のようになります。しかし、この構文は特定の文脈で曖昧さを生じさせました。
具体的には、Go言語のif
文やfor
文のブロックも波括弧 {}
を使用します。もし、型名が変数名や関数名と衝突した場合、コンパイラはT{...}
が型T
の複合リテラルなのか、あるいはT
という名前の変数や関数に関連するブロックなのかを区別するのが難しくなる可能性がありました。
例えば、以下のような状況を考えます(これはあくまで概念的な例であり、実際のGo言語のパーサーがどのように動作するかを厳密に反映しているわけではありませんが、曖昧さの概念を説明するものです)。
// もしTが型名でもあり、かつ変数名や関数名としても使われた場合
T {
// ここが複合リテラルの内容なのか、それともTに関連するコードブロックなのか?
}
このような曖昧さを避けるため、Go言語の設計者は複合リテラルの構文を丸括弧 ()
に変更することを決定しました。これにより、T(...)
という構文は明確に型T
の複合リテラルを示し、他の構文との衝突を避けることができます。この変更は、Go言語のパーサーをよりシンプルかつ堅牢にする上で不可欠でした。
配列連結演算子の廃止
Go言語の初期の設計では、文字列と同様に配列も +
演算子で連結できる可能性が検討されていました。しかし、この機能は最終的に実装されず、このコミットで仕様から削除されました。
配列は固定長であるため、a + b
のような配列連結は、a
とb
の要素をすべて含む新しい配列を生成し、そこに要素をコピーする必要があります。これは、特に大きな配列の場合、メモリ割り当てとコピーのオーバーヘッドが大きくなります。Go言語はパフォーマンスを重視しており、このような暗黙的な高コスト操作は避ける傾向があります。
また、Go言語にはスライスという、より柔軟で効率的な動的配列の概念があります。スライスを使用すれば、append
関数などを用いて要素を追加したり、複数のスライスを結合したりすることが可能です。スライスは基になる配列を参照するため、不要なコピーを避けることができます。配列の連結演算子を廃止し、スライスとappend
関数に一本化することで、言語の複雑さを減らし、よりGoらしいイディオムを促進しました。
unsafe
パッケージ関数のコンパイル時定数化
unsafe
パッケージのAlignof
, Offsetof
, Sizeof
関数は、Goの型システムをバイパスして、特定の型のメモリレイアウトに関する情報(アライメント、フィールドのオフセット、サイズ)をバイト単位で返します。
このコミットで、これらの関数の結果が「コンパイル時定数」であると明記されました。これは以下の意味を持ちます。
- 最適化: コンパイラは、これらの関数の呼び出しをコンパイル時に計算し、その結果を直接コードに埋め込むことができます。これにより、実行時の計算オーバーヘッドがなくなります。
- 予測可能性: これらの値は、プログラムの実行環境や実行時の状態に依存せず、常に同じ値を取ることが保証されます。これは、低レベルなシステムプログラミングや、特定のメモリレイアウトに依存するコードを書く際に非常に重要です。
- 型安全性とのバランス:
unsafe
パッケージは型安全性を犠牲にしますが、その結果がコンパイル時定数であるという保証は、少なくともこれらの情報が実行時に不確定な要素とならないことを意味し、ある程度の予測可能性と制御を提供します。
コアとなるコードの変更箇所
このコミットの主要な変更は、doc/go_spec.txt
ファイル内の以下の部分に集中しています。
-
複合リテラルの文法定義の変更:
--- a/doc/go_spec.txt +++ b/doc/go_spec.txt @@ -1820,7 +1821,7 @@ Literals for composite data structures consist of the type of the value followed by a braced expression list for array, slice, and structure literals, or a list of expression pairs for map literals. - CompositeLit = LiteralType "{" [ ( ExpressionList | ExprPairList ) [ "," ] ] "}" . + CompositeLit = LiteralType "(" [ ( ExpressionList | ExprPairList ) [ "," ] ] ")" . LiteralType = Type | "[" "..." "]" ElementType . ExprPairList = ExprPair { "," ExprPair } . ExprPair = Expression ":" Expression .
CompositeLit
の定義で、波括弧{}
が丸括弧()
に変更されています。 -
複合リテラルの使用例の更新:
--- a/doc/go_spec.txt +++ b/doc/go_spec.txt @@ -1839,7 +1840,7 @@ Given one can write - pi := Num{Rat{22, 7}, 3.14159, "pi"}; + pi := Num(Rat(22, 7), 3.14159, "pi"); The length of an array literal is the length specified in the LiteralType. If fewer elements than the length are provided in the literal, the missing @@ -1848,24 +1849,24 @@ It is an error to provide more elements than specified in LiteralType. The notation "..." may be used in place of the length expression to denote a length equal to the number of elements in the literal. - buffer := [10]string{}; // len(buffer) == 10 - primes := [6]int{2, 3, 5, 7, 9, 11}; // len(primes) == 6 - days := [...]string{"sat", "sun"}; // len(days) == 2 + buffer := [10]string(); // len(buffer) == 10 + primes := [6]int(2, 3, 5, 7, 9, 11); // len(primes) == 6 + days := [...]string("sat", "sun"); // len(days) == 2 A slice literal is a slice describing the entire underlying array literal. Thus, the length and capacity of a slice literal is the number of elements provided in the literal. A slice literal of the form - []T{x1, x2, ... xn}\n + []T(x1, x2, ... xn)\n is essentially a shortcut for a slice operation applied to an array literal: - [n]T{x1, x2, ... xn}[0 : n]\n + [n]T(x1, x2, ... xn)[0 : n]\n Map literals are similar except the elements of the expression list are key-value pairs separated by a colon: - m := map[string]int{"good": 0, "bad": 1, "indifferent": 7}; + m := map[string]int("good": 0, "bad": 1, "indifferent": 7);
複合リテラルの様々な例が、波括弧
{}
から丸括弧()
を使用するように変更されています。 -
配列連結に関する記述の削除:
--- a/doc/go_spec.txt +++ b/doc/go_spec.txt @@ -2282,14 +2283,11 @@ to strings and arrays; all other arithmetic operators apply to integer types onl << left shift integer << unsigned integer >> right shift integer >> unsigned integer -Strings and arrays can be concatenated using the "+" operator -(or via the "+=" assignment): +Strings can be concatenated using the "+" operator (or the "+=" assignment): s := "hi" + string(c) - a += []int{5, 6, 7} -String and array addition creates a new array or string by copying the -elements. +String addition creates a new string by copying the elements. For integer values, "/" and "%" satisfy the following relationship:
配列の連結に関する記述と例 (
a += []int{5, 6, 7}
) が削除されています。 -
unsafe
パッケージに関する記述の追加:--- a/doc/go_spec.txt +++ b/doc/go_spec.txt @@ -3532,6 +3525,9 @@ a variable "x" of the largest arithmetic type (8 for a float64), but may be smaller on systems that have less stringent alignment restrictions or are space constrained. +The results of calls to "unsafe.Alignof", "unsafe.Offsetof", and +"unsafe.Sizeof" are compile-time constants.\n + Size and alignment guarantees ----
unsafe.Alignof
,unsafe.Offsetof
,unsafe.Sizeof
の結果がコンパイル時定数であるという記述が追加されています。
コアとなるコードの解説
このコミットは、Go言語の仕様書であるdoc/go_spec.txt
に対する変更であり、実際のGoコンパイラやランタイムのコード変更ではありません。しかし、仕様書の変更は、Go言語の構文とセマンティクスがどのように定義され、将来のコンパイラ実装にどのように影響するかを直接示しています。
-
複合リテラルの構文変更:
CompositeLit = LiteralType "{" ... "}"
からCompositeLit = LiteralType "(" ... ")"
への変更は、Go言語のパーサーが複合リテラルを認識する方法を根本的に変えることを意味します。これにより、パーサーは波括弧{}
を見たときに、それがコードブロックなのか複合リテラルなのかを判断する際の曖昧さが解消され、よりシンプルで効率的な解析が可能になります。これは、言語の設計段階で構文の明確性を追求した結果です。 -
配列連結演算子の廃止: 配列の連結に
+
演算子を使用する記述が削除されたことは、Go言語が配列の連結にこの演算子をサポートしないことを明確にしています。これは、Go言語が固定長配列の操作を明示的に行わせる方針であり、暗黙的なコピーを伴う操作を避けるという設計思想を反映しています。代わりに、スライスとappend
関数が推奨されるイディオムとなります。 -
unsafe
パッケージのドキュメント更新:unsafe
パッケージの関数が返す値がコンパイル時定数であるという記述は、これらの関数がGoプログラムの実行時に動的にメモリレイアウトを計算するのではなく、コンパイル時に静的に決定されることを保証します。これは、Go言語のコンパイラがこれらの情報を利用して、より効率的な機械語コードを生成できることを示唆しています。例えば、構造体のフィールドへのアクセスを最適化する際に、オフセットがコンパイル時に既知であれば、実行時の計算を省略できます。
これらの変更は、Go言語がまだ初期段階にあった頃に、言語の堅牢性、パフォーマンス、そして開発者の使いやすさを向上させるために行われた重要な設計上の決定を反映しています。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語の仕様書: https://go.dev/ref/spec (現在の最新版)
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語の初期の設計に関する議論 (Go Mailing List archivesなど、一般には公開されていない可能性が高いが、当時の設計決定の背景を理解する上で重要)
- Go言語のブログ (初期の言語変更に関する記事がある可能性): https://go.dev/blog/
- Go言語の
unsafe
パッケージに関するドキュメント: https://pkg.go.dev/unsafe - Go言語の複合リテラルに関するドキュメント: https://go.dev/ref/spec#Composite_literals
- Go言語の配列とスライスに関するドキュメント: https://go.dev/blog/slices-intro
- Go言語の歴史に関する記事や講演 (例: "The Evolution of Go" by Robert Griesemer, "Go at Google: Language Design in the Service of Software Engineering" by Rob Pike)
これらの情報は、Go言語の設計思想と、なぜ特定の設計決定がなされたのかを深く理解するのに役立ちます。