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

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

このコミットは、Go言語の公式仕様書である doc/go_spec.html ファイルに対する変更です。このファイルは、Go言語の構文、セマンティクス、および組み込み機能に関する詳細な定義を提供し、言語の動作を正確に記述する役割を担っています。

コミット

このコミットは、Go言語の型変換(conversions)に関する仕様記述を明確にし、特に「等しい型(equal types)」間の変換を明示的に許可するように変更しました。これにより、型変換と型アサーション(当時の用語では「型ガード」)の間の区別がより明確になりました。

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

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

元コミット内容

commit e8b43190bbec4b3b445739c216a0f4e023a9442f
Author: Russ Cox <rsc@golang.org>
Date:   Tue Mar 3 08:10:25 2009 -0800

    clarify conversions vs type guards.
    allow conversions between equal types.
    
    R=r
    DELTA=15  (4 added, 4 deleted, 7 changed)
    OCL=25618
    CL=25630
---
 doc/go_spec.html | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n

変更の背景

Go言語の初期段階において、型システムに関する仕様はまだ進化途上にありました。特に、ある型から別の型への値の変換(型変換)と、インターフェース型に格納された値の基底型を動的にチェックし、より具体的な型として扱う(型アサーション、当時のGoの文脈では「型ガード」と呼ばれていた可能性が高い)という二つの概念の区別が、仕様書上で曖昧であったと考えられます。

このコミットの背景には、以下の点が挙げられます。

  1. 概念の明確化: 型変換と型アサーションは、Go言語における型操作の基本的な要素ですが、その目的と動作は異なります。型変換は値の表現を変更するものであり、型アサーションはインターフェースの動的な型を検査するものです。これらの概念が混同されると、プログラマが意図しない動作を引き起こしたり、コードの理解を妨げたりする可能性があります。このコミットは、仕様書においてこれらの概念を明確に区別することを目的としています。
  2. 「等しい型」間の変換の明示: Go言語では、異なる型名であっても、その基底型や構造が同じであれば「等しい型」と見なされる場合があります(例:type MyInt intint)。このような等しい型間での変換が、仕様上どのように扱われるべきかについて、初期の仕様では明示されていなかった可能性があります。このコミットは、等しい型間の変換が常に成功することを明記することで、言語の予測可能性と一貫性を高めようとしています。
  3. 仕様の成熟: 言語が開発されるにつれて、より厳密で包括的な仕様が求められます。初期の仕様書には、将来的な検討事項を示す「TODO」コメントが含まれていることがよくあります。このコミットは、型変換に関するTODOコメントを解消し、仕様をより完成されたものにする一環でもあります。

前提知識の解説

このコミットを理解するためには、Go言語の以下の基本的な型システムに関する知識が必要です。

1. 型(Types)

Go言語のすべての値は特定の型を持ちます。型は、値がどのような種類のデータであるか、そしてその値に対してどのような操作が可能であるかを定義します。Goには、以下のような様々な型があります。

  • 基本型(Basic Types): 整数型(int, int8, uint, uint64など)、浮動小数点型(float32, float64)、真偽値型(bool)、文字列型(string)など。
  • 複合型(Composite Types): 配列([N]T)、スライス([]T)、構造体(struct)、ポインタ(*T)、関数(func)、インターフェース(interface)、マップ(map)、チャネル(chan)など。

2. 型変換(Conversions)

型変換は、ある型の値を別の型の値に明示的に変換する操作です。Goでは、T(expression) の形式で記述されます。ここで T は変換先の型、expression は変換元の値です。型変換は、値のビットパターンを解釈し直したり、異なる表現形式に変換したりする際に使用されます。

例:

var i int = 42
var f float64 = float64(i) // intからfloat64への変換
var s string = string(65)  // 整数からUTF-8文字への変換 ("A")

型変換が可能なのは、Goの仕様で定められた特定の型間の組み合わせに限られます。例えば、整数型と浮動小数点型の間、文字列とバイトスライス/バイト配列の間などです。

3. 型アサーション(Type Assertions)

型アサーションは、インターフェース型に格納されている動的な値の基底型を検査し、その値を特定の具体的な型として抽出する操作です。Goでは、interfaceValue.(Type) の形式で記述されます。

例:

var i interface{} = "hello"
s, ok := i.(string) // iがstring型であればsに値が入り、okはtrue
if ok {
    fmt.Println(s)
}

型アサーションは、インターフェースの動的な型が期待する型と一致しない場合、パニック(panic)を引き起こす可能性があります(二値返却形式を使用しない場合)。

4. 「型ガード(Type Guards)」という用語について

このコミットメッセージにある「type guards」という用語は、Go言語の現在の公式ドキュメントや仕様書では一般的に使用されていません。これは、Go言語の初期開発段階で一時的に使われていた用語であるか、あるいは他のプログラミング言語(例:TypeScript)における「型ガード」の概念(実行時に型の絞り込みを行うメカニズム)を指している可能性があります。Go言語におけるこれに相当する機能は、前述の「型アサーション」です。このコミットは、型変換と型アサーション(型ガード)の概念的な区別を明確にすることを意図しています。

5. 「等しい型(Equal Types)」について

Go言語において、2つの型が「等しい」と見なされるのは、以下のいずれかの条件を満たす場合です。

  • 型が同じ型名である。
  • 型が構造的に等しい(例:同じ要素型と長さを持つ配列、同じフィールド名と型を持つ構造体、同じシグネチャを持つ関数など)。

特に、型定義によって新しい型が作成された場合でも、その基底型が同じであれば、型変換によって相互に変換できる場合があります。例えば、type MyInt intint は異なる型ですが、MyInt(someInt)int(someMyInt) のように変換可能です。このコミットは、このような「等しい型」間の変換が常に成功することを明示しています。

技術的詳細

このコミットは、doc/go_spec.html の「Conversions」セクションにおける記述を具体的に変更しています。

1. 型変換の対象範囲の拡大

変更前は、型変換の対象となる T(変換先の型)が「算術型または文字列」に限定されていました。

<p>
-where <code>T</code> is the type name of an arithmetic type or string (§Basic types),
-and <code>value</code> is the value of an expression that can be converted to a value
+where <code>T</code> is a type
+and <code>value</code> is an expression
+that can be converted to a value
of result type <code>T</code>.
</p>

この変更により、T は「任意の型」となり、型変換の概念がより広範な型に適用されることが示唆されました。これは、Goの型システムが進化し、より多様な型間での変換が考慮されるようになったことを反映しています。

2. 「等しい型」間の変換ルールの追加

最も重要な変更点は、型変換のルールリストに新しい項目が追加されたことです。

変更前(抜粋):

<ul>
<li>
-1) Between integer types.  If the value is a signed quantity, it is
+1) Between equal types.  The conversion always succeeds.
+</li>
+<li>
+2) Between integer types.  If the value is a signed quantity, it is

変更後(抜粋):

<ul>
<li>
1) Between equal types. The conversion always succeeds.
</li>
<li>
2) Between integer types. If the value is a signed quantity, it is

新しいルール 1) Between equal types. The conversion always succeeds. が追加されました。これは、Go言語において、型が等しいと見なされる場合(例:type MyInt intint のように、異なる型名でも基底型が同じ場合)、それらの型間での明示的な型変換が常に成功することを明確にしています。これにより、プログラマは等しい型間での変換の挙動について、より明確な保証を得られるようになりました。

3. 既存ルールの番号変更

新しいルールが追加されたため、既存のルールは番号が一つずつずらされました。

  • 1) Between integer types. → 新 2) Between integer types.
  • 2) Between integer and floating point types... → 新 3) Between integer and floating point types...
  • 3) Strings permit two special conversions. → 新 4) Strings permit two special conversions.
  • 3a) → 新 4a)
  • 3b) → 新 4b)

4. TODOコメントの削除

インターフェース/ポインタ変換と型ガードに関するTODOコメントが削除されました。

-<font color=red>\n-TODO: Do we allow interface/ptr conversions in this form or do they\n-have to be written as type guards? (§Type guards)\n-</font>

このTODOコメントの削除は、このコミットによって型変換と型ガード(型アサーション)の区別が明確になり、インターフェースやポインタに関する変換の疑問が解消されたことを示唆しています。これは、Go言語の型システムがより成熟し、これらの概念が仕様書上で適切に定義された結果と言えます。

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

このコミットは、doc/go_spec.html ファイルの以下の部分を変更しています。

  1. 型変換の定義部分:

    --- a/doc/go_spec.html
    +++ b/doc/go_spec.html
    @@ -3609,32 +3609,36 @@ T(value)
     </pre>
      
     <p>
    -where <code>T</code> is the type name of an arithmetic type or string (§Basic types),
    -and <code>value</code> is the value of an expression that can be converted to a value
    +where <code>T</code> is a type
    +and <code>value</code> is an expression
    +that can be converted to a value
     of result type <code>T</code>.
     <p>
     The following conversion rules apply:
     </p>
    
    • where <code>T</code> is the type name of an arithmetic type or string (§Basic types), が削除され、
    • where <code>T</code> is a type が追加されました。
  2. 型変換のルールリスト:

    --- a/doc/go_spec.html
    +++ b/doc/go_spec.html
    @@ -3609,32 +3609,36 @@ T(value)
     </pre>
      
     <p>
    -where <code>T</code> is the type name of an arithmetic type or string (§Basic types),
    -and <code>value</code> is the value of an expression that can be converted to a value
    +where <code>T</code> is a type
    +and <code>value</code> is an expression
    +that can be converted to a value
     of result type <code>T</code>.
     <p>
     The following conversion rules apply:
     </p>
     <ul>
     <li>
    -1) Between integer types.  If the value is a signed quantity, it is
    +1) Between equal types.  The conversion always succeeds.
    +</li>
    +<li>
    +2) Between integer types.  If the value is a signed quantity, it is
     sign extended to implicit infinite precision; otherwise it is zero
     extended.  It is then truncated to fit in the result type size.
     For example, <code>uint32(int8(0xFF))</code> is <code>0xFFFFFFFF</code>.\n The conversion always yields a valid value; there is no signal for overflow.
     </li>
     <li>
    -2) Between integer and floating point types, or between floating point
    +3) Between integer and floating point types, or between floating point
     types.  To avoid overdefining the properties of the conversion, for
     now it is defined as a ``best effort\'\' conversion.  The conversion
     always succeeds but the value may be a NaN or other problematic
     result. <font color=red>TODO: clarify?</font>
     </li>
     <li>
    -3) Strings permit two special conversions.\n+4) Strings permit two special conversions.
     </li>
     <li>
    -3a) Converting an integer value yields a string containing the UTF-8
    +4a) Converting an integer value yields a string containing the UTF-8
     representation of the integer.
     (TODO: this one could be done just as well by a library.)
      
    @@ -3644,7 +3648,7 @@ string(0x65e5)  // \"\\u65e5\"\n \n </li>
     <li>
    -3b) Converting an array or slice of bytes yields a string whose successive
    +4b) Converting an array or slice of bytes yields a string whose successive
     bytes are those of the array/slice.
      
     <pre>
    @@ -3658,10 +3662,6 @@ There is no linguistic mechanism to convert between pointers and integers.\n The <code>unsafe</code> package\n implements this functionality under\n restricted circumstances (§Package <code>unsafe</code>).\n-<font color=red>\n-TODO: Do we allow interface/ptr conversions in this form or do they\n-have to be written as type guards? (§Type guards)\n-</font>\n </p>
    
    • 新しい <li>1) Between equal types. The conversion always succeeds.</li> が追加されました。
    • 既存のルール番号が 1) から 2)2) から 3)3) から 4) に変更されました。
    • 3a)3b) もそれぞれ 4a)4b) に変更されました。
  3. TODOコメントの削除:

    --- a/doc/go_spec.html
    +++ b/doc/go_spec.html
    @@ -3658,10 +3662,6 @@ There is no linguistic mechanism to convert between pointers and integers.\n The <code>unsafe</code> package\n implements this functionality under\n restricted circumstances (§Package <code>unsafe</code>).\n-<font color=red>\n-TODO: Do we allow interface/ptr conversions in this form or do they\n-have to be written as type guards? (§Type guards)\n-</font>\n </p>
    
    • TODO: Do we allow interface/ptr conversions in this form or do they have to be written as type guards? (§Type guards) というコメントが削除されました。

コアとなるコードの解説

このコミットの核心は、Go言語の型変換に関する仕様の厳密性と明確性を向上させることにあります。

  1. 型変換の汎用化: 変更前は、型変換の対象となる T が「算術型または文字列」に限定されていました。これは、Goの型変換が主に数値型や文字列型の間で行われることを示唆していました。しかし、Goの型システムはより柔軟であり、ユーザー定義型や複合型間でも特定の条件下で変換が可能です。 where <code>T</code> is a type への変更は、型変換の概念がより広範な型に適用されることを明示し、仕様の記述をより汎用的にしました。これにより、将来的にGo言語が進化し、より多くの型間での変換がサポートされるようになった場合でも、この仕様記述が対応できるようになります。

  2. 「等しい型」間の変換の明文化: 新しく追加された 1) Between equal types. The conversion always succeeds. は、Go言語の型システムにおける重要な側面を明確にしています。Goでは、型名が異なっていても、その基底型や構造が同じであれば、それらの型は「等しい」と見なされることがあります。例えば、type MyInt intint は異なる型ですが、両者は基底型が int であるため、構造的に等しいと見なされます。 このルールが追加されることで、MyInt(someInt)int(someMyInt) のような変換が常に成功することが保証されます。これは、プログラマが型を定義する際に、その型が基底型とどのように相互作用するかをより正確に理解するのに役立ちます。また、コンパイラの実装者にとっても、このような変換の挙動が明確になるため、一貫性のある実装が可能になります。

  3. 型変換と型アサーションの区別: 削除されたTODOコメントは、インターフェースやポインタに関する変換が、通常の型変換として扱われるべきか、それとも「型ガード」(型アサーション)として扱われるべきかという疑問を呈していました。このTODOコメントの削除は、このコミットによって、型変換と型アサーションの概念が明確に区別され、それぞれの適用範囲が仕様書上で適切に定義されたことを示しています。 Go言語では、インターフェース型から具体的な型への変換は型アサーション(value.(Type))によって行われ、これは実行時の型チェックを伴います。一方、型変換(Type(value))は、主にコンパイル時に型が既知である場合に、値の表現を変更するために使用されます。このコミットは、これらの異なるメカニズムの役割を仕様書上で明確にすることで、言語のセマンティクスをより厳密に定義しました。

全体として、このコミットはGo言語の型システムに関する仕様をより正確で、包括的で、理解しやすいものにすることを目的としています。これは、言語の安定性と予測可能性を高める上で重要なステップでした。

関連リンク

  • Go言語の公式ドキュメント: https://go.dev/doc/
  • Go言語の仕様書(現在のバージョン): https://go.dev/ref/spec
    • 特に「Conversions」のセクションを参照すると、このコミットで変更された内容の現在の形を確認できます。

参考にした情報源リンク

  • Go言語の公式仕様書(コミット時点のバージョンに近いもの、または現在のバージョン)
  • Go言語の型システムに関する一般的な解説記事やチュートリアル
  • Go言語の型変換と型アサーションに関するドキュメント
  • Go言語の初期の設計に関する議論やメーリングリストのアーカイブ(もし公開されているものがあれば)