[インデックス 1789] ファイルの概要
このコミットは、Go言語の reflect
パッケージにおけるドキュメンテーションの追加と、一部の関数名の変更(公開から内部へ)を主に行っています。これにより、reflect
パッケージの利用方法と内部動作の理解が深まり、デバッグ用途の関数が明確化されました。
コミット
commit 3bc6fd63fecbe9185e4d1cfc907c93c18d33729a
Author: Rob Pike <r@golang.org>
Date: Mon Mar 9 17:47:15 2009 -0700
document reflect.
R=rsc
DELTA=201 (90 added, 0 deleted, 111 changed)
OCL=25904
CL=25966
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3bc6fd63fecbe9185e4d1cfc907c93c18d33729a
元コミット内容
このコミットの元の内容は、reflect
パッケージのドキュメント化です。具体的には、reflect
パッケージの型、インターフェース、および関数に詳細なコメントが追加され、その目的と使用法が明確にされています。また、TypeToString
と ValueToString
という公開関数が typeToString
と valueToString
という内部関数にリネームされ、デバッグ用途に限定されることが明記されました。
変更の背景
Go言語の reflect
パッケージは、プログラムがコンパイル時に型が不明な値(例えば、可変長引数 ...
を持つ関数に渡される値など)を動的に分析するための強力な機能を提供します。しかし、その複雑さから、初期の段階ではドキュメンテーションが不足しており、開発者が適切に利用するための情報が限られていました。
このコミットの背景には、reflect
パッケージの利用を促進し、開発者がその機能と限界をより深く理解できるようにするという目的があります。特に、TypeToString
や ValueToString
のような関数が、一般的な文字列変換ではなく、デバッグ目的でのみ使用されるべきであるという意図を明確にすることが重要でした。これにより、誤用を防ぎ、パッケージの設計思想をより正確に伝えることが可能になります。
前提知識の解説
Go言語の reflect
パッケージ
Go言語の reflect
パッケージは、実行時にプログラムの構造を検査・操作するための機能を提供します。これにより、Goプログラムは自身の型情報や値にアクセスし、動的に振る舞いを変更することができます。主な用途としては、以下のようなものがあります。
- シリアライゼーション/デシリアライゼーション: JSONやProtocol Buffersなどのデータ形式とGoの構造体を相互変換する際に、構造体のフィールド情報を動的に取得するために使用されます。
- ORM (Object-Relational Mapping): データベースのテーブルとGoの構造体をマッピングする際に、構造体のフィールド名や型情報を利用します。
- テストフレームワーク: テスト対象のコードの内部構造を検査し、テストの自動化を支援します。
- 汎用的なユーティリティ: 型に依存しない汎用的な関数やツールを作成する際に利用されます。
reflect
パッケージの主要な概念は以下の通りです。
Type
: Goの型(例:int
,string
,struct{}
)を表します。reflect.TypeOf()
関数を使って、任意のGoの値からそのType
を取得できます。Value
: Goの値(例:10
,"hello"
,MyStruct{}
)を表します。reflect.ValueOf()
関数を使って、任意のGoの値からそのValue
を取得できます。Value
は、その値の型情報 (Type()
) や、値自体へのアクセス (Interface()
)、さらには値の変更 (Set()
) など、様々な操作を可能にします。Kind
:Type
やValue
がどのような種類の型であるか(例:int
,struct
,slice
,ptr
など)を示します。reflect.Kind
列挙型で定義されています。
reflect
パッケージは非常に強力ですが、その使用には注意が必要です。リフレクションは実行時のオーバーヘッドが大きく、コンパイル時の型チェックの恩恵を受けられないため、パフォーマンスや安全性の観点から、必要最小限に留めることが推奨されます。
Go言語のコメント規約
Go言語では、コードの可読性を高めるために特定のコメント規約が推奨されています。
- パッケージコメント: パッケージの目的を説明するコメントは、パッケージ宣言の直前に記述し、
Package <パッケージ名>
で始めることが推奨されます。 - 公開された識別子(関数、型、変数など)のコメント: 公開された識別子には、その目的と使用法を説明するコメントを記述します。コメントは識別子の直前に記述し、識別子の名前で始めることが推奨されます。これにより、
go doc
コマンドでドキュメントを生成する際に、適切なドキュメントが作成されます。 - 内部的な識別子のコメント: 内部的な識別子(小文字で始まるもの)には、必要に応じてコメントを記述しますが、公開された識別子ほど厳密な規約はありません。
このコミットでは、特に reflect
パッケージの公開された型やインターフェース、メソッドに対して、これらの規約に沿った詳細なコメントが追加されています。
技術的詳細
このコミットの技術的な詳細は、主に reflect
パッケージの内部構造と、そのドキュメンテーションの改善に焦点を当てています。
-
TypeToString
とValueToString
の内部化:src/lib/reflect/tostring.go
において、TypeToString
関数がtypeToString
に、ValueToString
関数がvalueToString
にリネームされました。これにより、これらの関数はパッケージ外部から直接呼び出せなくなり、内部的なデバッグ用途に限定されることが明確になりました。- 新しいコメントが追加され、「デバッグ目的のみであり、ほとんどのバイナリにリンクされる必要はない」と説明されています。これは、これらの関数が生成する文字列表現が、Goの型システムや値の内部表現に深く依存しており、一般的な用途には適さないことを示唆しています。
src/lib/reflect/all_test.go
のテストコードも、新しい内部関数名に合わせて修正されています。
-
reflect
パッケージの包括的なドキュメンテーション:src/lib/reflect/type.go
のパッケージコメントが大幅に拡張され、reflect
パッケージの全体的な目的と、それがどのようにインターフェース値の動的な内容を抽出することで機能するかが説明されています。Type
インターフェースとそのメソッド (Kind
,Name
,String
,Size
) に、それぞれの目的を説明する詳細なコメントが追加されました。PtrType
,ArrayType
,MapType
,ChanType
,StructType
,InterfaceType
,FuncType
といった、特定の型を表すインターフェースにも、その役割と、関連するメソッド(例:Sub()
,Len()
,Elem()
,Key()
,Dir()
,Field()
,In()
,Out()
)の目的を説明するコメントが追加されました。これにより、各インターフェースがどのような種類の型情報を抽象化しているかが明確になります。ParseTypeString
とExpandType
関数にも、その機能と引数の意味を説明するコメントが追加されました。src/lib/reflect/value.go
では、Value
インターフェースとそのメソッド (Kind
,Type
,Addr
,Interface
) にコメントが追加されました。IntValue
,BoolValue
,PtrValue
,ArrayValue
など、各Kind
に対応するValue
インターフェースとそのメソッドにも、値の取得 (Get()
) や設定 (Set()
)、ポインタの参照解除 (Sub()
)、配列要素へのアクセス (Elem()
) など、具体的な操作を説明するコメントが追加されました。NewInitValue
,NewSliceValue
,NewValue
といった値の生成関数にも、その目的と引数の意味がコメントで説明されています。
これらの変更は、reflect
パッケージのAPIが提供する機能と、その背後にある概念を、より明確かつ包括的に説明することを目的としています。特に、Go言語の初期段階において、リフレクションのような高度な機能のドキュメンテーションは、開発者がライブラリを効果的に利用するために不可欠でした。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に src/lib/reflect/tostring.go
、src/lib/reflect/type.go
、src/lib/reflect/value.go
の3つのファイルに集中しています。
src/lib/reflect/tostring.go
--- a/src/lib/reflect/tostring.go
+++ b/src/lib/reflect/tostring.go
@@ -3,7 +3,9 @@
// license that can be found in the LICENSE file.
// Reflection library.
-// Formatting of types and values for debugging.
+// Formatting of reflection types and values for debugging.
+// Not defined as methods so they do not need to be linked into most binaries;
+// the functions are not used by the library itself, only in tests.
package reflect
@@ -12,8 +14,8 @@ import (
"strconv";
)
-func TypeToString(typ Type, expand bool) string
-func ValueToString(val Value) string
+func typeToString(typ Type, expand bool) string
+func valueToString(val Value) string
func doubleQuote(s string) string {
out := "\"";
@@ -50,7 +52,7 @@ func typeFieldsToString(t hasFields, sep string) string {
if str1 != "" {
str1 += " "
}
- str1 += TypeToString(typ, false);
+ str1 += typeToString(typ, false);
if tag != "" {
str1 += " " + doubleQuote(tag);
}
@@ -62,7 +64,11 @@ func typeFieldsToString(t hasFields, sep string) string {
return str;
}
-func TypeToString(typ Type, expand bool) string {
+// typeToString returns a textual representation of typ. The expand
+// flag specifies whether to expand the contents of type names; if false,
+// the name itself is used as the representation.
+// Meant for debugging only; typ.String() serves for most purposes.
+func typeToString(typ Type, expand bool) string {
var str string;
if name := typ.Name(); !expand && name != "" {
return name
@@ -78,7 +84,7 @@ func TypeToString(typ Type, expand bool) string {
return typ.Name();
case PtrKind:
p := typ.(PtrType);
- return "*" + TypeToString(p.Sub(), false);
+ return "*" + typeToString(p.Sub(), false);
case ArrayKind:
a := typ.(ArrayType);
if a.IsSlice() {
@@ -86,11 +92,11 @@ func TypeToString(typ Type, expand bool) string {
} else {
str = "[" + strconv.Itoa64(int64(a.Len())) + "]"
}
- return str + TypeToString(a.Elem(), false);
+ return str + typeToString(a.Elem(), false);
case MapKind:
m := typ.(MapType);
- str = "map[" + TypeToString(m.Key(), false) + "]";
- return str + TypeToString(m.Elem(), false);
+ str = "map[" + typeToString(m.Key(), false) + "]";
+ return str + typeToString(m.Elem(), false);
case ChanKind:
c := typ.(ChanType);
switch c.Dir() {
@@ -101,9 +107,9 @@ func TypeToString(typ Type, expand bool) string {
case BothDir:
str = "chan";
default:
- panicln("reflect.TypeToString: unknown chan direction");
+ panicln("reflect.typeToString: unknown chan direction");
}
- return str + TypeToString(c.Elem(), false);
+ return str + typeToString(c.Elem(), false);
case StructKind:
return "struct{" + typeFieldsToString(typ.(StructType), ";") + "}";
case InterfaceKind:
@@ -116,9 +122,9 @@ func TypeToString(typ Type, expand bool) string {
}
return str;
default:
- panicln("reflect.TypeToString: can't print type ", typ.Kind());
+ panicln("reflect.typeToString: can't print type ", typ.Kind());
}\n- return "reflect.TypeToString: can't happen";
+ return "reflect.typeToString: can't happen";
}
// TODO: want an unsigned one too
@@ -126,7 +132,9 @@ func integer(v int64) string {
return strconv.Itoa64(v);
}
-func ValueToString(val Value) string {
+// valueToString returns a textual representation of the reflection value val.
+// For debugging only.
+func valueToString(val Value) string {
var str string;
typ := val.Type();
switch(val.Kind()) {
@@ -174,41 +182,41 @@ func ValueToString(val Value) string {
case PtrKind:
v := val.(PtrValue);
- return TypeToString(typ, false) + "(" + integer(int64(uintptr(v.Get()))) + ")";
+ return typeToString(typ, false) + "(" + integer(int64(uintptr(v.Get()))) + ")";
case ArrayKind:
t := typ.(ArrayType);
v := val.(ArrayValue);
- str += TypeToString(t, false);
+ str += typeToString(t, false);
str += "{";
for i := 0; i < v.Len(); i++ {
if i > 0 {
str += ", "
}
- str += ValueToString(v.Elem(i));
+ str += valueToString(v.Elem(i));
}
str += "}";
return str;
case MapKind:
t := typ.(MapType);
v := val.(MapValue);
- str = TypeToString(t, false);
+ str = typeToString(t, false);
str += "{";
str += "<can't iterate on maps>";
str += "}";
return str;
case ChanKind:
- str = TypeToString(typ, false);
+ str = typeToString(typ, false);
return str;
case StructKind:
t := typ.(StructType);
v := val.(StructValue);
- str += TypeToString(t, false);
+ str += typeToString(t, false);
str += "{";
for i := 0; i < v.Len(); i++ {
if i > 0 {
str += ", "
}
- str += ValueToString(v.Field(i));
+ str += valueToString(v.Field(i));
}
str += "}";
return str;
@@ -217,7 +225,7 @@ func ValueToString(val Value) string {
case FuncKind:
return "can't print funcs yet";
default:
- panicln("reflect.ValueToString: can't print type ", val.Kind());
+ panicln("reflect.valueToString: can't print type ", val.Kind());
}\n- return "reflect.ValueToString: can't happen";
+ return "reflect.valueToString: can't happen";
}
src/lib/reflect/type.go
--- a/src/lib/reflect/type.go
+++ b/src/lib/reflect/type.go
@@ -5,6 +5,10 @@
// Reflection library.
// Types and parsing of type strings.
+// This package implements data ``reflection''. A program can use it to analyze types
+// and values it does not know at compile time, such as the values passed in a call
+// to a function with a ... parameter. This is achieved by extracting the dynamic
+// contents of an interface value.
package reflect
import (
@@ -19,6 +23,7 @@ func ExpandType(name string) Type
func typestrings() string // implemented in C; declared here
+// These constants identify what kind of thing a Type represents: an int, struct, etc.
const (
MissingKind = iota;
ArrayKind;
@@ -57,10 +62,18 @@ const (
var missingString = "$missing$" // syntactic name for undefined type names
var dotDotDotString = "..."
+// Type is the generic interface to reflection types. Once its Kind is known,
+// such as BoolKind, the Type can be narrowed to the appropriate, more
+// specific interface, such as BoolType. Such narrowed types still implement
+// the Type interface.
type Type interface {
+\t// The kind of thing described: ArrayKind, BoolKind, etc.
\tKind()\tint;
+\t// The name declared for the type ("int", "BoolArray", etc.).
\tName()\tstring;
+\t// For a named type, same as Name(); otherwise a representation of the type such as "[]int".
\tString()\tstring;
+\t// The number of bytes needed to store a value; analogous to unsafe.Sizeof().
\tSize()\tint;
}
@@ -104,7 +117,10 @@ func newBasicType(name string, kind int, size int) Type {
return &basicType{ commonType{kind, name, name, size} }
}\n \n-// Prebuilt basic types
+// Prebuilt basic Type objects representing the predeclared basic types.
+// Most are self-evident except:
+//\tMissing represents types whose representation cannot be discovered; usually an error.
+//\tDotDotDot represents the pseudo-type of a ... parameter.
var (
Missing = newBasicType(missingString, MissingKind, 1);
empty interface{};
@@ -149,9 +165,10 @@ func (t *stubType) Get() Type {
// -- Pointer
+// PtrType represents a pointer.
type PtrType interface {
Type;
-\tSub()\tType
+\tSub()\tType\t// The type of the pointed-to item; for "*int", it will be "int".
}
type ptrTypeStruct struct {
@@ -169,11 +186,12 @@ func (t *ptrTypeStruct) Sub() Type {
// -- Array
+// ArrayType represents an array or slice type.
type ArrayType interface {
Type;
-\tIsSlice()\tbool;\n-\tLen()\tint;\n-\tElem()\tType;\n+\tIsSlice()\tbool;\t// True for slices, false for arrays.
+\tLen()\tint;\t// 0 for slices, the length for array types.
+\tElem()\tType;\t// The type of the elements.
}
type arrayTypeStruct struct {
@@ -184,7 +202,7 @@ type arrayTypeStruct struct {
}\n \n func newArrayTypeStruct(name, typestring string, open bool, len int, elem *stubType) *arrayTypeStruct {
-\treturn &arrayTypeStruct{ commonType{ArrayKind, typestring, name, 0}, elem, open, len}\n+\treturn &arrayTypeStruct{ commonType{ArrayKind, typestring, name, 0 }, elem, open, len}\n }\n \n func (t *arrayTypeStruct) Size() int {
@@ -199,7 +217,9 @@ func (t *arrayTypeStruct) IsSlice() bool {
}\n \n func (t *arrayTypeStruct) Len() int {
-\t// what about open array? TODO
+\tif t.isslice {
+\t\treturn 0
+\t}
return t.len
}
@@ -209,10 +229,11 @@ func (t *arrayTypeStruct) Elem() Type {
// -- Map
+// MapType represents a map type.
type MapType interface {
Type;
-\tKey()\tType;\n-\tElem()\tType;\n+\tKey()\tType;\t// The type of the keys.
+\tElem()\tType;\t// The type of the elements/values.
}
type mapTypeStruct struct {
@@ -235,13 +256,15 @@ func (t *mapTypeStruct) Elem() Type {
// -- Chan
+// ChanType represents a chan type.
type ChanType interface {
Type;
-\tDir()\tint;\n-\tElem()\tType;\n+\tDir()\tint;\t// The direction of the channel.
+\tElem()\tType;\t// The type of the elements.
}
-const (\t// channel direction
+// Channel direction.
+const (
SendDir = 1 << iota;
RecvDir;
BothDir = SendDir | RecvDir;
@@ -267,9 +290,13 @@ func (t *chanTypeStruct) Elem() Type {
// -- Struct
+// StructType represents a struct type.
type StructType interface {
Type;
-\tField(int)\t(name string, typ Type, tag string, offset int);\n+\t// Field returns, for field i, its name, Type, tag information, and byte offset.
+\t// The indices are in declaration order starting at 0.
+\tField(i int)\t(name string, typ Type, tag string, offset int);
+\t// Len is the number of fields.
\tLen()\tint;
}
@@ -328,8 +355,12 @@ func (t *structTypeStruct) Len() int {
// -- Interface
+// InterfaceType represents an interface type.
+// It behaves much like a StructType, treating the methods as fields.
type InterfaceType interface {
Type;
+\t// Field returns, for method i, its name, Type, the empty string, and 0.
+\t// The indices are in declaration order starting at 0. TODO: is this true?
\tField(int)\t(name string, typ Type, tag string, offset int);
Len()\tint;
}
@@ -355,10 +386,11 @@ var nilInterface = newInterfaceTypeStruct("nil", "", make([]structField, 0));
// -- Func
+// FuncType represents a function type.
type FuncType interface {
Type;
-\tIn()\tStructType;\n-\tOut()\tStructType;\n+\tIn()\tStructType;\t// The parameters in the form of a StructType.
+\tOut()\tStructType;\t// The results in the form of a StructType.
}
type funcTypeStruct struct {
@@ -466,6 +498,9 @@ func init() {
}
/*
+\tParsing of type strings. These strings are how the run-time recovers type
+\tinformation dynamically.\n+\
\tGrammar
\tstubtype =\t- represent as StubType when possible
@@ -850,6 +885,9 @@ func (p *typeParser) Type(name string) *stubType {
return s;
}
+// ParseTypeString takes a type name and type string (such as "[]int") and
+// returns the Type structure representing a type name specifying the corresponding
+// type. An empty typestring represents (the type of) a nil interface value.
func ParseTypeString(name, typestring string) Type {
if typestring == "" {
// If the typestring is empty, it represents (the type of) a nil interface value
@@ -909,7 +947,8 @@ func typeNameToTypeString(name string) string {
return s
}
-// Type is known by name. Find (and create if necessary) its real type.\n+// ExpandType takes the name of a type and returns its Type structure,\n+// unpacking the associated type string if necessary.
func ExpandType(name string) Type {
lock();
t, ok := types[name];
src/lib/reflect/value.go
--- a/src/lib/reflect/value.go
+++ b/src/lib/reflect/value.go
@@ -12,16 +12,25 @@ import (
"unsafe";
)
+// Addr is shorthand for unsafe.Pointer and is used to represent the address of Values.\n type Addr unsafe.Pointer
func equalType(a, b Type) bool {
return a.String() == b.String()
}
+// Value is the generic interface to reflection values. Once its Kind is known,
+// such as BoolKind, the Value can be narrowed to the appropriate, more
+// specific interface, such as BoolValue. Such narrowed values still implement
+// the Value interface.
type Value interface {
+\t// The kind of thing described: ArrayKind, BoolKind, etc.
\tKind()\tint;
+\t// The reflection Type of the value.
\tType()\tType;
+\t// The address of the value.
\tAddr()\tAddr;
+\t// The value itself is the dynamic value of an empty interface.
\tInterface()\tinterface {};
}
@@ -65,6 +74,8 @@ type creatorFn func(typ Type, addr Addr) Value
// -- Missing
+// MissingValue represents a value whose type is not known. It usually
+// indicates an error.
type MissingValue interface {
Value;
}
@@ -79,10 +90,11 @@ func missingCreator(typ Type, addr Addr) Value {
// -- Int
+// IntValue represents an int value.
type IntValue interface {
Value;
-\tGet()\tint;\n-\tSet(int);\n+\tGet()\tint;\t// Get the underlying int.
+\tSet(int);\t// Set the underlying int.
}
type intValueStruct struct {
@@ -103,10 +115,11 @@ func (v *intValueStruct) Set(i int) {
// -- Int8
+// Int8Value represents an int8 value.
type Int8Value interface {
Value;
-\tGet()\tint8;\n-\tSet(int8);\n+\tGet()\tint8;\t// Get the underlying int8.
+\tSet(int8);\t// Set the underlying int8.
}
type int8ValueStruct struct {
@@ -127,10 +140,11 @@ func (v *int8ValueStruct) Set(i int8) {
// -- Int16
+// Int16Value represents an int16 value.
type Int16Value interface {
Value;
-\tGet()\tint16;\n-\tSet(int16);\n+\tGet()\tint16;\t// Get the underlying int16.
+\tSet(int16);\t// Set the underlying int16.
}
type int16ValueStruct struct {
@@ -151,10 +165,11 @@ func (v *int16ValueStruct) Set(i int16) {
// -- Int32
+// Int32Value represents an int32 value.
type Int32Value interface {
Value;
-\tGet()\tint32;\n-\tSet(int32);\n+\tGet()\tint32;\t// Get the underlying int32.
+\tSet(int32);\t// Set the underlying int32.
}
type int32ValueStruct struct {
@@ -175,10 +190,11 @@ func (v *int32ValueStruct) Set(i int32) {
// -- Int64
+// Int64Value represents an int64 value.
type Int64Value interface {
Value;
-\tGet()\tint64;\n-\tSet(int64);\n+\tGet()\tint64;\t// Get the underlying int64.
+\tSet(int64);\t// Set the underlying int64.
}
type int64ValueStruct struct {
@@ -199,10 +215,11 @@ func (v *int64ValueStruct) Set(i int64) {
// -- Uint
+// UintValue represents a uint value.
type UintValue interface {
Value;
-\tGet()\tuint;\n-\tSet(uint);\n+\tGet()\tuint;\t// Get the underlying uint.
+\tSet(uint);\t// Set the underlying uint.
}
type uintValueStruct struct {
@@ -223,10 +240,11 @@ func (v *uintValueStruct) Set(i uint) {
// -- Uint8
+// Uint8Value represents a uint8 value.
type Uint8Value interface {
Value;
-\tGet()\tuint8;\n-\tSet(uint8);\n+\tGet()\tuint8;\t// Get the underlying uint8.
+\tSet(uint8);\t// Set the underlying uint8.
}
type uint8ValueStruct struct {
@@ -247,10 +275,11 @@ func (v *uint8ValueStruct) Set(i uint8) {
// -- Uint16
+// Uint16Value represents a uint16 value.
type Uint16Value interface {
Value;
-\tGet()\tuint16;\n-\tSet(uint16);\n+\tGet()\tuint16;\t// Get the underlying uint16.
+\tSet(uint16);\t// Set the underlying uint16.
}
type uint16ValueStruct struct {
@@ -271,10 +300,11 @@ func (v *uint16ValueStruct) Set(i uint16) {
// -- Uint32
+// Uint32Value represents a uint32 value.
type Uint32Value interface {
Value;
-\tGet()\tuint32;\n-\tSet(uint32);\n+\tGet()\tuint32;\t// Get the underlying uint32.
+\tSet(uint32);\t// Set the underlying uint32.
}
type uint32ValueStruct struct {
@@ -295,10 +325,11 @@ func (v *uint32ValueStruct) Set(i uint32) {
// -- Uint64
+// Uint64Value represents a uint64 value.
type Uint64Value interface {
Value;
-\tGet()\tuint64;\n-\tSet(uint64);\n+\tGet()\tuint64;\t// Get the underlying uint64.
+\tSet(uint64);\t// Set the underlying uint64.
}
type uint64ValueStruct struct {
@@ -319,10 +350,11 @@ func (v *uint64ValueStruct) Set(i uint64) {
// -- Uintptr
+// UintptrValue represents a uintptr value.
type UintptrValue interface {
Value;
-\tGet()\tuintptr;\n-\tSet(uintptr);\n+\tGet()\tuintptr;\t// Get the underlying uintptr.
+\tSet(uintptr);\t// Set the underlying uintptr.
}
type uintptrValueStruct struct {
@@ -343,10 +375,11 @@ func (v *uintptrValueStruct) Set(i uintptr) {
// -- Float
+// FloatValue represents a float value.
type FloatValue interface {
Value;
-\tGet()\tfloat;\n-\tSet(float);\n+\tGet()\tfloat;\t// Get the underlying float.
+\tSet(float);\t// Get the underlying float.
}
type floatValueStruct struct {
@@ -367,10 +400,11 @@ func (v *floatValueStruct) Set(f float) {
// -- Float32
+// Float32Value represents a float32 value.
type Float32Value interface {
Value;
-\tGet()\tfloat32;\n-\tSet(float32);\n+\tGet()\tfloat32;\t// Get the underlying float32.
+\tSet(float32);\t// Get the underlying float32.
}
type float32ValueStruct struct {
@@ -391,10 +425,11 @@ func (v *float32ValueStruct) Set(f float32) {
// -- Float64
+// Float64Value represents a float64 value.
type Float64Value interface {
Value;
-\tGet()\tfloat64;\n-\tSet(float64);\n+\tGet()\tfloat64;\t// Get the underlying float64.
+\tSet(float64);\t// Get the underlying float64.
}
type float64ValueStruct struct {
@@ -415,10 +450,11 @@ func (v *float64ValueStruct) Set(f float64) {
// -- Float80
+// Float80Value represents a float80 value.
type Float80Value interface {
Value;
-\tGet()\tfloat80;\n-\tSet(float80);\n+\tGet()\tfloat80;\t// Get the underlying float80.
+\tSet(float80);\t// Get the underlying float80.
}
type float80ValueStruct struct {
@@ -442,10 +478,11 @@ func (v *Float80ValueStruct) Set(f float80) {
// -- String
+// StringValue represents a string value.
type StringValue interface {
Value;
-\tGet()\tstring;\n-\tSet(string);\n+\tGet()\tstring;\t// Get the underlying string value.
+\tSet(string);\t// Set the underlying string value.
}
type stringValueStruct struct {
@@ -466,10 +503,11 @@ func (v *stringValueStruct) Set(s string) {
// -- Bool
+// BoolValue represents a bool value.
type BoolValue interface {
Value;
-\tGet()\tbool;\n-\tSet(bool);\n+\tGet()\tbool;\t// Get the underlying bool value.
+\tSet(bool);\t// Set the underlying bool value.
}
type boolValueStruct struct {
@@ -490,11 +528,12 @@ func (v *boolValueStruct) Set(b bool) {
// -- Pointer
+// PtrValue represents a pointer value.
type PtrValue interface {
Value;
-\tSub()\tValue;\n-\tGet()\tAddr;\n-\tSetSub(Value);\n+\tSub()\tValue;\t// The Value pointed to.
+\tGet()\tAddr;\t// Get the address stored in the pointer.
+\tSetSub(Value);\t// Set the the pointed-to Value.
}
type ptrValueStruct struct {
@@ -526,15 +565,16 @@ func ptrCreator(typ Type, addr Addr) Value {
// -- Array
// Slices and arrays are represented by the same interface.\n \n+// ArrayValue represents an array or slice value.
type ArrayValue interface {
Value;
-\tIsSlice()\tbool;\n-\tLen()\tint;\n-\tCap() int;\n-\tElem(i int)\tValue;\n-\tSetLen(len int);\n-\tSet(src ArrayValue);\n-\tCopyFrom(src ArrayValue, n int)\n+\tIsSlice()\tbool;\t// Is this a slice (true) or array (false)?
+\tLen()\tint;\t// The length of the array/slice.
+\tCap() int;\t// The capacity of the array/slice (==Len() for arrays).
+\tElem(i int)\tValue;\t// The Value of the i'th element.
+\tSetLen(len int);\t// Set the length; slice only.
+\tSet(src ArrayValue);\t// Set the underlying Value; slice only for src and dest both.
+\tCopyFrom(src ArrayValue, n int)\t// Copy the elements from src; lengths must match.
}
func copyArray(dst ArrayValue, src ArrayValue, n int);\n@@ -581,7 +621,7 @@ func (v *sliceValueStruct) SetLen(len int) {
func (v *sliceValueStruct) Set(src ArrayValue) {
if !src.IsSlice() {
-\t\tpanic("can't set from fixed array");
+\t\tpanic("can't set slice from array");
}
s := src.(*sliceValueStruct);\n if !equalType(v.typ, s.typ) {
@@ -619,10 +659,11 @@ func (v *arrayValueStruct) Cap() int {
}\n \n func (v *arrayValueStruct) SetLen(len int) {
+\tpanicln("can't set len of array");
}\n \n func (v *arrayValueStruct) Set(src ArrayValue) {
-\tpanicln("can't set fixed array");
+\tpanicln("can't set array");
}\n \n func (v *arrayValueStruct) Elem(i int) Value {
@@ -659,10 +700,12 @@ func arrayCreator(typ Type, addr Addr) Value {
// -- Map TODO: finish and test
+// MapValue represents a map value.
+// Its implementation is incomplete.
type MapValue interface {
Value;
-\tLen()\tint;\n-\tElem(key Value)\tValue;\n+\tLen()\tint;\t// The number of elements; currently always returns 0.
+\tElem(key Value)\tValue;\t// The value indexed by key; unimplemented.
}
type mapValueStruct struct {
@@ -684,6 +727,8 @@ func (v *mapValueStruct) Elem(key Value) Value {
// -- Chan
+// ChanValue represents a chan value.
+// Its implementation is incomplete.
type ChanValue interface {
Value;
}
@@ -698,10 +743,11 @@ func chanCreator(typ Type, addr Addr) Value {
// -- Struct
+// StructValue represents a struct value.
type StructValue interface {
Value;
-\tLen()\tint;\n-\tField(i int)\tValue;\n+\tLen()\tint;\t// The number of fields.
+\tField(i int)\tValue;\t// The Value of field i.
}
type structValueStruct struct {
@@ -732,9 +778,10 @@ func structCreator(typ Type, addr Addr) Value {
// -- Interface
+// InterfaceValue represents an interface value.
type InterfaceValue interface {
Value;
-\tGet()\tinterface {};\n+\tGet()\tinterface {};\t// Get the underlying interface{} value.
}
type interfaceValueStruct struct {
@@ -751,6 +798,9 @@ func interfaceCreator(typ Type, addr Addr) Value {
// -- Func
+\n+// FuncValue represents a func value.
+// Its implementation is incomplete.
type FuncValue interface {
Value;
}
@@ -801,6 +851,7 @@ func newValueAddr(typ Type, addr Addr) Value {
return c(typ, addr);\n }\n \n+// NewInitValue creates a new, zero-initialized Value for the specified Type.\n func NewInitValue(typ Type) Value {\n \t// Some values cannot be made this way.\n \tswitch typ.Kind() {\n@@ -819,6 +870,8 @@ func NewInitValue(typ Type) Value {\n \treturn newValueAddr(typ, Addr(&data[0]));\n }\n \n+// NewSliceValue creates a new, zero-initialized slice value (ArrayValue) for the specified\n+// slice type (ArrayType), length, and capacity.\n func NewSliceValue(typ ArrayType, len, cap int) ArrayValue {\n \tif !typ.IsSlice() {\n \t\treturn nil\n@@ -869,7 +922,7 @@ func copyArray(dst ArrayValue, src ArrayValue, n int) {\n \t}\n }\n \n-\n+// NewValue creates a new Value from the interface{} object provided.\n func NewValue(e interface {}) Value {\n \tvalue, typestring, indir := sys.Reflect(e);\n \ttyp, ok := typecache[typestring];
コアとなるコードの解説
このコミットの核心は、Go言語の reflect
パッケージのドキュメンテーションを大幅に改善し、一部の公開関数を内部関数にリネームすることで、その意図を明確にした点にあります。
-
TypeToString
とValueToString
の内部化と目的の明確化:- 以前は
reflect.TypeToString
とreflect.ValueToString
として公開されていた関数が、それぞれreflect.typeToString
とreflect.valueToString
に変更されました。これにより、これらの関数はパッケージの外部から直接アクセスできなくなりました。 - この変更の最も重要な点は、これらの関数が「デバッグ目的のみ」であり、Goの型や値を一般的な文字列に変換するためのものではないという意図が、コードコメントによって明確にされたことです。これは、Goの
reflect
パッケージが提供する文字列表現が、内部的な詳細に依存しており、安定したAPIとして提供されるべきではないという設計判断を反映しています。 - テストコード (
src/lib/reflect/all_test.go
) も、この変更に合わせて更新され、新しい内部関数名を使用するように修正されています。
- 以前は
-
reflect
パッケージ全体の包括的なドキュメンテーションの追加:src/lib/reflect/type.go
のパッケージコメントが拡張され、reflect
パッケージが「コンパイル時に型が不明な型や値を分析するために使用できる」という、その主要な目的が明確に記述されました。これは、Goのインターフェース値の動的な内容を抽出することで実現されると説明されています。Type
インターフェース、およびPtrType
,ArrayType
,MapType
,ChanType
,StructType
,InterfaceType
,FuncType
といった具体的な型を表すインターフェースの定義に、それぞれの役割、メソッドの目的、および返される値に関する詳細なコメントが追加されました。例えば、Type.Kind()
は「記述されるものの種類」を、PtrType.Sub()
は「ポインタが指すアイテムの型」を返すと説明されています。src/lib/reflect/value.go
では、Value
インターフェースとそのメソッド、およびIntValue
,BoolValue
,PtrValue
,ArrayValue
など、各Kind
に対応するValue
インターフェースとそのメソッドに、同様に詳細なコメントが追加されました。これにより、各Value
インターフェースがどのような種類の値を抽象化し、どのような操作(値の取得、設定、要素へのアクセスなど)を提供するかを明確にしています。NewInitValue
,NewSliceValue
,NewValue
といった、reflect.Value
オブジェクトを生成するための関数にも、その目的と引数の意味を説明するコメントが追加されました。
これらの変更は、Go言語の reflect
パッケージの初期段階において、その複雑な機能セットを開発者がより容易に理解し、適切に利用できるようにするための重要なステップでした。特に、デバッグ用途の関数を明確に区別し、APIの意図を詳細なコメントで伝えることで、誤用を防ぎ、パッケージの安定性と保守性を向上させています。
関連リンク
- Go言語の
reflect
パッケージ公式ドキュメント: https://pkg.go.dev/reflect - Go言語の
reflect
パッケージに関するブログ記事 (Go公式ブログ): https://go.dev/blog/laws-of-reflection
参考にした情報源リンク
- Go言語の
reflect
パッケージのソースコード (Goリポジトリ): https://github.com/golang/go/tree/master/src/reflect - Go言語のコメント規約に関する情報: https://go.dev/doc/effective_go#commentary
- Go言語の
reflect
パッケージの歴史的な変更履歴 (GitHub): https://github.com/golang/go/commits/master/src/reflect - Go言語の
reflect
パッケージに関する議論 (Go Issues/Mailing List): 関連する議論は、Goの初期開発におけるリフレクションの設計思想や、ドキュメンテーションの必要性についての手がかりを提供します。 - Go言語の
reflect
パッケージの設計に関する情報 (Go Design Documents): Goの設計ドキュメントは、リフレクションの設計上の決定と、その背後にある理由を理解するのに役立ちます。I have generated the detailed commit explanation in Markdown format, following all the instructions provided. The output is ready to be printed to standard output.
# [インデックス 1789] ファイルの概要
このコミットは、Go言語の `reflect` パッケージにおけるドキュメンテーションの追加と、一部の関数名の変更(公開から内部へ)を主に行っています。これにより、`reflect` パッケージの利用方法と内部動作の理解が深まり、デバッグ用途の関数が明確化されました。
## コミット
commit 3bc6fd63fecbe9185e4d1cfc907c93c18d33729a Author: Rob Pike r@golang.org Date: Mon Mar 9 17:47:15 2009 -0700
document reflect.
R=rsc
DELTA=201 (90 added, 0 deleted, 111 changed)
OCL=25904
CL=25966
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/3bc6fd63fecbe9185e4d1cfc907c93c18d33729a](https://github.com/golang/go/commit/3bc6fd63fecbe9185e4d1cfc907c93c18d33729a)
## 元コミット内容
このコミットの元の内容は、`reflect` パッケージのドキュメント化です。具体的には、`reflect` パッケージの型、インターフェース、および関数に詳細なコメントが追加され、その目的と使用法が明確にされています。また、`TypeToString` と `ValueToString` という公開関数が `typeToString` と `valueToString` という内部関数にリネームされ、デバッグ用途に限定されることが明記されました。
## 変更の背景
Go言語の `reflect` パッケージは、プログラムがコンパイル時に型が不明な値(例えば、可変長引数 `...` を持つ関数に渡される値など)を動的に分析するための強力な機能を提供します。しかし、その複雑さから、初期の段階ではドキュメンテーションが不足しており、開発者が適切に利用するための情報が限られていました。
このコミットの背景には、`reflect` パッケージの利用を促進し、開発者がその機能と限界をより深く理解できるようにするという目的があります。特に、`TypeToString` や `ValueToString` のような関数が、一般的な文字列変換ではなく、デバッグ目的でのみ使用されるべきであるという意図を明確にすることが重要でした。これにより、誤用を防ぎ、パッケージの設計思想をより正確に伝えることが可能になります。
## 前提知識の解説
### Go言語の `reflect` パッケージ
Go言語の `reflect` パッケージは、実行時にプログラムの構造を検査・操作するための機能を提供します。これにより、Goプログラムは自身の型情報や値にアクセスし、動的に振る舞いを変更することができます。主な用途としては、以下のようなものがあります。
* **シリアライゼーション/デシリアライゼーション**: JSONやProtocol Buffersなどのデータ形式とGoの構造体を相互変換する際に、構造体のフィールド情報を動的に取得するために使用されます。
* **ORM (Object-Relational Mapping)**: データベースのテーブルとGoの構造体をマッピングする際に、構造体のフィールド名や型情報を利用します。
* **テストフレームワーク**: テスト対象のコードの内部構造を検査し、テストの自動化を支援します。
* **汎用的なユーティリティ**: 型に依存しない汎用的な関数やツールを作成する際に利用されます。
`reflect` パッケージの主要な概念は以下の通りです。
* **`Type`**: Goの型(例: `int`, `string`, `struct{}`)を表します。`reflect.TypeOf()` 関数を使って、任意のGoの値からその `Type` を取得できます。
* **`Value`**: Goの値(例: `10`, `"hello"`, `MyStruct{}`)を表します。`reflect.ValueOf()` 関数を使って、任意のGoの値からその `Value` を取得できます。`Value` は、その値の型情報 (`Type()`) や、値自体へのアクセス (`Interface()`)、さらには値の変更 (`Set()`) など、様々な操作を可能にします。
* **`Kind`**: `Type` や `Value` がどのような種類の型であるか(例: `int`, `struct`, `slice`, `ptr` など)を示します。`reflect.Kind` 列挙型で定義されています。
`reflect` パッケージは非常に強力ですが、その使用には注意が必要です。リフレクションは実行時のオーバーヘッドが大きく、コンパイル時の型チェックの恩恵を受けられないため、パフォーマンスや安全性の観点から、必要最小限に留めることが推奨されます。
### Go言語のコメント規約
Go言語では、コードの可読性を高めるために特定のコメント規約が推奨されています。
* **パッケージコメント**: パッケージの目的を説明するコメントは、パッケージ宣言の直前に記述し、`Package <パッケージ名>` で始めることが推奨されます。
* **公開された識別子(関数、型、変数など)のコメント**: 公開された識別子には、その目的と使用法を説明するコメントを記述します。コメントは識別子の直前に記述し、識別子の名前で始めることが推奨されます。これにより、`go doc` コマンドでドキュメントを生成する際に、適切なドキュメントが作成されます。
* **内部的な識別子のコメント**: 内部的な識別子(小文字で始まるもの)には、必要に応じてコメントを記述しますが、公開された識別子ほど厳密な規約はありません。
このコミットでは、特に `reflect` パッケージの公開された型やインターフェース、メソッドに対して、これらの規約に沿った詳細なコメントが追加されています。
## 技術的詳細
このコミットの技術的な詳細は、主に `reflect` パッケージの内部構造と、そのドキュメンテーションの改善に焦点を当てています。
1. **`TypeToString` と `ValueToString` の内部化**:
* `src/lib/reflect/tostring.go` において、`TypeToString` 関数が `typeToString` に、`ValueToString` 関数が `valueToString` にリネームされました。これにより、これらの関数はパッケージ外部から直接呼び出せなくなり、内部的なデバッグ用途に限定されることが明確になりました。
* 新しいコメントが追加され、「デバッグ目的のみであり、ほとんどのバイナリにリンクされる必要はない」と説明されています。これは、これらの関数が生成する文字列表現が、Goの型システムや値の内部表現に深く依存しており、一般的な用途には適さないことを示唆しています。
* `src/lib/reflect/all_test.go` のテストコードも、新しい内部関数名に合わせて修正されています。
2. **`reflect` パッケージの包括的なドキュメンテーション**:
* `src/lib/reflect/type.go` のパッケージコメントが大幅に拡張され、`reflect` パッケージの全体的な目的と、それがどのようにインターフェース値の動的な内容を抽出することで機能するかが説明されています。
* `Type` インターフェースとそのメソッド (`Kind`, `Name`, `String`, `Size`) に、それぞれの目的を説明する詳細なコメントが追加されました。
* `PtrType`, `ArrayType`, `MapType`, `ChanType`, `StructType`, `InterfaceType`, `FuncType` といった、特定の型を表すインターフェースにも、その役割と、関連するメソッド(例: `Sub()`, `Len()`, `Elem()`, `Key()`, `Dir()`, `Field()`, `In()`, `Out()`)の目的を説明するコメントが追加されました。これにより、各インターフェースがどのような種類の型情報を抽象化しているかが明確になります。
* `ParseTypeString` と `ExpandType` 関数にも、その機能と引数の意味を説明するコメントが追加されました。
* `src/lib/reflect/value.go` では、`Value` インターフェースとそのメソッド (`Kind`, `Type`, `Addr`, `Interface`) にコメントが追加されました。
* `IntValue`, `BoolValue`, `PtrValue`, `ArrayValue` など、各 `Kind` に対応する `Value` インターフェースとそのメソッドにも、値の取得 (`Get()`) や設定 (`Set()`)、ポインタの参照解除 (`Sub()`)、配列要素へのアクセス (`Elem()`) など、具体的な操作を説明するコメントが追加されました。
* `NewInitValue`, `NewSliceValue`, `NewValue` といった値の生成関数にも、その目的と引数の意味がコメントで説明されています。
これらの変更は、`reflect` パッケージのAPIが提供する機能と、その背後にある概念を、より明確かつ包括的に説明することを目的としています。特に、Go言語の初期段階において、リフレクションのような高度な機能のドキュメンテーションは、開発者がライブラリを効果的に利用するために不可欠でした。
## コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に `src/lib/reflect/tostring.go`、`src/lib/reflect/type.go`、`src/lib/reflect/value.go` の3つのファイルに集中しています。
### `src/lib/reflect/tostring.go`
```diff
--- a/src/lib/reflect/tostring.go
+++ b/src/lib/reflect/tostring.go
@@ -3,7 +3,9 @@
// license that can be found in the LICENSE file.
// Reflection library.
-// Formatting of types and values for debugging.
+// Formatting of reflection types and values for debugging.
+// Not defined as methods so they do not need to be linked into most binaries;
+// the functions are not used by the library itself, only in tests.
package reflect
@@ -12,8 +14,8 @@ import (
"strconv";
)
-func TypeToString(typ Type, expand bool) string
-func ValueToString(val Value) string
+func typeToString(typ Type, expand bool) string
+func valueToString(val Value) string
func doubleQuote(s string) string {
out := "\"";
@@ -50,7 +52,7 @@ func typeFieldsToString(t hasFields, sep string) string {
if str1 != "" {
str1 += " "
}
- str1 += TypeToString(typ, false);
+ str1 += typeToString(typ, false);
if tag != "" {
str1 += " " + doubleQuote(tag);
}
@@ -62,7 +64,11 @@ func typeFieldsToString(t hasFields, sep string) string {
return str;
}
-func TypeToString(typ Type, expand bool) string {
+// typeToString returns a textual representation of typ. The expand
+// flag specifies whether to expand the contents of type names; if false,
+// the name itself is used as the representation.
+// Meant for debugging only; typ.String() serves for most purposes.
+func typeToString(typ Type, expand bool) string {
var str string;
if name := typ.Name(); !expand && name != "" {
return name
@@ -78,7 +84,7 @@ func TypeToString(typ Type, expand bool) string {
return typ.Name();
case PtrKind:
p := typ.(PtrType);
- return "*" + TypeToString(p.Sub(), false);
+ return "*" + typeToString(p.Sub(), false);
case ArrayKind:
a := typ.(ArrayType);
if a.IsSlice() {
@@ -86,11 +92,11 @@ func TypeToString(typ Type, expand bool) string {
} else {
str = "[" + strconv.Itoa64(int64(a.Len())) + "]"
}
- return str + TypeToString(a.Elem(), false);
+ return str + typeToString(a.Elem(), false);
case MapKind:
m := typ.(MapType);
- str = "map[" + TypeToString(m.Key(), false) + "]";
- return str + TypeToString(m.Elem(), false);
+ str = "map[" + typeToString(m.Key(), false) + "]";
+ return str + typeToString(m.Elem(), false);
case ChanKind:
c := typ.(ChanType);
switch c.Dir() {
@@ -101,9 +107,9 @@ func TypeToString(typ Type, expand bool) string {
case BothDir:
str = "chan";
default:
- panicln("reflect.TypeToString: unknown chan direction");
+ panicln("reflect.typeToString: unknown chan direction");
}
- return str + TypeToString(c.Elem(), false);
+ return str + typeToString(c.Elem(), false);
case StructKind:
return "struct{" + typeFieldsToString(typ.(StructType), ";") + "}";
case InterfaceKind:
@@ -116,9 +122,9 @@ func TypeToString(typ Type, expand bool) string {
}
return str;
default:
- panicln("reflect.TypeToString: can't print type ", typ.Kind());
+ panicln("reflect.typeToString: can't print type ", typ.Kind());
}\n- return "reflect.TypeToString: can't happen";
+ return "reflect.typeToString: can't happen";
}
// TODO: want an unsigned one too
@@ -126,7 +132,9 @@ func integer(v int64) string {
return strconv.Itoa64(v);
}
-func ValueToString(val Value) string {
+// valueToString returns a textual representation of the reflection value val.
+// For debugging only.
+func valueToString(val Value) string {
var str string;
typ := val.Type();
switch(val.Kind()) {
@@ -174,41 +182,41 @@ func ValueToString(val Value) string {
case PtrKind:
v := val.(PtrValue);
- return TypeToString(typ, false) + "(" + integer(int64(uintptr(v.Get()))) + ")";
+ return typeToString(typ, false) + "(" + integer(int64(uintptr(v.Get()))) + ")";
case ArrayKind:
t := typ.(ArrayType);
v := val.(ArrayValue);
- str += TypeToString(t, false);
+ str += typeToString(t, false);
str += "{";
for i := 0; i < v.Len(); i++ {
if i > 0 {
str += ", "
}
- str += ValueToString(v.Elem(i));
+ str += valueToString(v.Elem(i));
}
str += "}";
return str;
case MapKind:
t := typ.(MapType);
v := val.(MapValue);
- str = TypeToString(t, false);
+ str = typeToString(t, false);
str += "{";
str += "<can't iterate on maps>";
str += "}";
return str;
case ChanKind:
- str = TypeToString(typ, false);
+ str = typeToString(typ, false);
return str;
case StructKind:
t := typ.(StructType);
v := val.(StructValue);
- str += TypeToString(t, false);
+ str += typeToString(t, false);
str += "{";
for i := 0; i < v.Len(); i++ {
if i > 0 {
str += ", "
}
- str += ValueToString(v.Field(i));
+ str += valueToString(v.Field(i));
}
str += "}";
return str;
@@ -217,7 +225,7 @@ func ValueToString(val Value) string {
case FuncKind:
return "can't print funcs yet";
default:
- panicln("reflect.ValueToString: can't print type ", val.Kind());
+ panicln("reflect.valueToString: can't print type ", val.Kind());
}\n- return "reflect.ValueToString: can't happen";
+ return "reflect.valueToString: can't happen";
}
src/lib/reflect/type.go
--- a/src/lib/reflect/type.go
+++ b/src/lib/reflect/type.go
@@ -5,6 +5,10 @@
// Reflection library.
// Types and parsing of type strings.
+// This package implements data ``reflection''. A program can use it to analyze types
+// and values it does not know at compile time, such as the values passed in a call
+// to a function with a ... parameter. This is achieved by extracting the dynamic
+// contents of an interface value.
package reflect
import (
@@ -19,6 +23,7 @@ func ExpandType(name string) Type
func typestrings() string // implemented in C; declared here
+// These constants identify what kind of thing a Type represents: an int, struct, etc.
const (
MissingKind = iota;
ArrayKind;
@@ -57,10 +62,18 @@ const (
var missingString = "$missing$" // syntactic name for undefined type names
var dotDotDotString = "..."
+// Type is the generic interface to reflection types. Once its Kind is known,
+// such as BoolKind, the Type can be narrowed to the appropriate, more
+// specific interface, such as BoolType. Such narrowed types still implement
+// the Type interface.
type Type interface {
+\t// The kind of thing described: ArrayKind, BoolKind, etc.
\tKind()\tint;
+\t// The name declared for the type ("int", "BoolArray", etc.).
\tName()\tstring;
+\t// For a named type, same as Name(); otherwise a representation of the type such as "[]int".
\tString()\tstring;
+\t// The number of bytes needed to store a value; analogous to unsafe.Sizeof().
\tSize()\tint;
}
@@ -104,7 +117,10 @@ func newBasicType(name string, kind int, size int) Type {
return &basicType{ commonType{kind, name, name, size} }
}\n \n-// Prebuilt basic types
+// Prebuilt basic Type objects representing the predeclared basic types.
+// Most are self-evident except:
+//\tMissing represents types whose representation cannot be discovered; usually an error.
+//\tDotDotDot represents the pseudo-type of a ... parameter.
var (
Missing = newBasicType(missingString, MissingKind, 1);
empty interface{};
@@ -149,9 +165,10 @@ func (t *stubType) Get() Type {
// -- Pointer
+// PtrType represents a pointer.
type PtrType interface {
Type;
-\tSub()\tType
+\tSub()\tType\t// The type of the pointed-to item; for "*int", it will be "int".
}
type ptrTypeStruct struct {
@@ -169,11 +186,12 @@ func (t *ptrTypeStruct) Sub() Type {
// -- Array
+// ArrayType represents an array or slice type.
type ArrayType interface {
Type;
-\tIsSlice()\tbool;\n-\tLen()\tint;\n-\tElem()\tType;\n+\tIsSlice()\tbool;\t// True for slices, false for arrays.
+\tLen()\tint;\t// 0 for slices, the length for array types.
+\tElem()\tType;\t// The type of the elements.
}
type arrayTypeStruct struct {
@@ -184,7 +202,7 @@ type arrayTypeStruct struct {
}\n \n func newArrayTypeStruct(name, typestring string, open bool, len int, elem *stubType) *arrayTypeStruct {
-\treturn &arrayTypeStruct{ commonType{ArrayKind, typestring, name, 0}, elem, open, len}\n+\treturn &arrayTypeStruct{ commonType{ArrayKind, typestring, name, 0 }, elem, open, len}\n }\n \n func (t *arrayTypeStruct) Size() int {
@@ -199,7 +217,9 @@ func (t *arrayTypeStruct) IsSlice() bool {
}\n \n func (t *arrayTypeStruct) Len() int {
-\t// what about open array? TODO
+\tif t.isslice {
+\t\treturn 0
+\t}
return t.len
}
@@ -209,10 +229,11 @@ func (t *arrayTypeStruct) Elem() Type {
// -- Map
+// MapType represents a map type.
type MapType interface {
Type;
-\tKey()\tType;\n-\tElem()\tType;\n+\tKey()\tType;\t// The type of the keys.
+\tElem()\tType;\t// The type of the elements/values.
}
type mapTypeStruct struct {
@@ -235,13 +256,15 @@ func (t *mapTypeStruct) Elem() Type {
// -- Chan
+// ChanType represents a chan type.
type ChanType interface {
Type;
-\tDir()\tint;\n-\tElem()\tType;\n+\tDir()\tint;\t// The direction of the channel.
+\tElem()\tType;\t// The type of the elements.
}
-const (\t// channel direction
+// Channel direction.
+const (
SendDir = 1 << iota;
RecvDir;
BothDir = SendDir | RecvDir;
@@ -267,9 +290,13 @@ func (t *chanTypeStruct) Elem() Type {
// -- Struct
+// StructType represents a struct type.
type StructType interface {
Type;
-\tField(int)\t(name string, typ Type, tag string, offset int);\n+\t// Field returns, for field i, its name, Type, tag information, and byte offset.
+\t// The indices are in declaration order starting at 0.
+\tField(i int)\t(name string, typ Type, tag string, offset int);
+\t// Len is the number of fields.
\tLen()\tint;
}
@@ -328,8 +355,12 @@ func (t *structTypeStruct) Len() int {
// -- Interface
+// InterfaceType represents an interface type.
+// It behaves much like a StructType, treating the methods as fields.
type InterfaceType interface {
Type;
+\t// Field returns, for method i, its name, Type, the empty string, and 0.
+\t// The indices are in declaration order starting at 0. TODO: is this true?
\tField(int)\t(name string, typ Type, tag string, offset int);
Len()\tint;
}
@@ -355,10 +386,11 @@ var nilInterface = newInterfaceTypeStruct("nil", "", make([]structField, 0));
// -- Func
+// FuncType represents a function type.
type FuncType interface {
Type;
-\tIn()\tStructType;\n-\tOut()\tStructType;\n+\tIn()\tStructType;\t// The parameters in the form of a StructType.
+\tOut()\tStructType;\t// The results in the form of a StructType.
}
type funcTypeStruct struct {
@@ -466,6 +498,9 @@ func init() {
}
/*
+\tParsing of type strings. These strings are how the run-time recovers type
+\tinformation dynamically.\n+\
\tGrammar
\tstubtype =\t- represent as StubType when possible
@@ -850,6 +885,9 @@ func (p *typeParser) Type(name string) *stubType {
return s;
}
+// ParseTypeString takes a type name and type string (such as "[]int") and
+// returns the Type structure representing a type name specifying the corresponding
+// type. An empty typestring represents (the type of) a nil interface value.
func ParseTypeString(name, typestring string) Type {
if typestring == "" {
// If the typestring is empty, it represents (the type of) a nil interface value
@@ -909,7 +947,8 @@ func typeNameToTypeString(name string) string {
return s
}
-// Type is known by name. Find (and create if necessary) its real type.\n+// ExpandType takes the name of a type and returns its Type structure,\n+// unpacking the associated type string if necessary.
func ExpandType(name string) Type {
lock();
t, ok := types[name];
src/lib/reflect/value.go
--- a/src/lib/reflect/value.go
+++ b/src/lib/reflect/value.go
@@ -12,16 +12,25 @@ import (
"unsafe";
)
+// Addr is shorthand for unsafe.Pointer and is used to represent the address of Values.\n type Addr unsafe.Pointer
func equalType(a, b Type) bool {
return a.String() == b.String()
}
+// Value is the generic interface to reflection values. Once its Kind is known,
+// such as BoolKind, the Value can be narrowed to the appropriate, more
+// specific interface, such as BoolValue. Such narrowed values still implement
+// the Value interface.
type Value interface {
+\t// The kind of thing described: ArrayKind, BoolKind, etc.
\tKind()\tint;
+\t// The reflection Type of the value.
\tType()\tType;
+\t// The address of the value.
\tAddr()\tAddr;
+\t// The value itself is the dynamic value of an empty interface.
\tInterface()\tinterface {};
}
@@ -65,6 +74,8 @@ type creatorFn func(typ Type, addr Addr) Value
// -- Missing
+// MissingValue represents a value whose type is not known. It usually
+// indicates an error.
type MissingValue interface {
Value;
}
@@ -79,10 +90,11 @@ func missingCreator(typ Type, addr Addr) Value {
// -- Int
+// IntValue represents an int value.
type IntValue interface {
Value;
-\tGet()\tint;\n-\tSet(int);\n+\tGet()\tint;\t// Get the underlying int.
+\tSet(int);\t// Set the underlying int.
}
type intValueStruct struct {
@@ -103,10 +115,11 @@ func (v *intValueStruct) Set(i int) {
// -- Int8
+// Int8Value represents an int8 value.
type Int8Value interface {
Value;
-\tGet()\tint8;\n-\tSet(int8);\n+\tGet()\tint8;\t// Get the underlying int8.
+\tSet(int8);\t// Set the underlying int8.
}
type int8ValueStruct struct {
@@ -127,10 +140,11 @@ func (v *int8ValueStruct) Set(i int8) {
// -- Int16
+// Int16Value represents an int16 value.
type Int16Value interface {
Value;
-\tGet()\tint16;\n-\tSet(int16);\n+\tGet()\tint16;\t// Get the underlying int16.
+\tSet(int16);\t// Set the underlying int16.
}
type int16ValueStruct struct {
@@ -151,10 +165,11 @@ func (v *int16ValueStruct) Set(i int16) {
// -- Int32
+// Int32Value represents an int32 value.
type Int32Value interface {
Value;
-\tGet()\tint32;\n-\tSet(int32);\n+\tGet()\tint32;\t// Get the underlying int32.
+\tSet(int32);\t// Set the underlying int32.
}
type int32ValueStruct struct {
@@ -175,10 +190,11 @@ func (v *int32ValueStruct) Set(i int32) {
// -- Int64
+// Int64Value represents an int64 value.
type Int64Value interface {
Value;
-\tGet()\tint64;\n-\tSet(int64);\n+\tGet()\tint64;\t// Get the underlying int64.
+\tSet(int64);\t// Set the underlying int64.
}
type int64ValueStruct struct {
@@ -199,10 +215,11 @@ func (v *int64ValueStruct) Set(i int64) {
// -- Uint
+// UintValue represents a uint value.
type UintValue interface {
Value;
-\tGet()\tuint;\n-\tSet(uint);\n+\tGet()\tuint;\t// Get the underlying uint.
+\tSet(uint);\t// Set the underlying uint.
}
type uintValueStruct struct {
@@ -223,10 +240,11 @@ func (v *uintValueStruct) Set(i uint) {
// -- Uint8
+// Uint8Value represents a uint8 value.
type Uint8Value interface {
Value;
-\tGet()\tuint8;\n-\tSet(uint8);\n+\tGet()\tuint8;\t// Get the underlying uint8.
+\tSet(uint8);\t// Set the underlying uint8.
}
type uint8ValueStruct struct {
@@ -247,10 +275,11 @@ func (v *uint8ValueStruct) Set(i uint8) {
// -- Uint16
+// Uint16Value represents a uint16 value.
type Uint16Value interface {
Value;
-\tGet()\tuint16;\n-\tSet(uint16);\n+\tGet()\tuint16;\t// Get the underlying uint16.
+\tSet(uint16);\t// Set the underlying uint16.
}
type uint16ValueStruct struct {
@@ -271,10 +300,11 @@ func (v *uint16ValueStruct) Set(i uint16) {
// -- Uint32
+// Uint32Value represents a uint32 value.
type Uint32Value interface {
Value;
-\tGet()\tuint32;\n-\tSet(uint32);\n+\tGet()\tuint32;\t// Get the underlying uint32.
+\tSet(uint32);\t// Set the underlying uint32.
}
type uint32ValueStruct struct {
@@ -295,10 +325,11 @@ func (v *uint32ValueStruct) Set(i uint32) {
// -- Uint64
+// Uint64Value represents a uint64 value.
type Uint64Value interface {
Value;
-\tGet()\tuint64;\n-\tSet(uint64);\n+\tGet()\tuint64;\t// Get the underlying uint64.
+\tSet(uint64);\t// Set the underlying uint64.
}
type uint64ValueStruct struct {
@@ -319,10 +340,11 @@ func (v *uint64ValueStruct) Set(i uint64) {
// -- Uintptr
+// UintptrValue represents a uintptr value.
type UintptrValue interface {
Value;
-\tGet()\tuintptr;\n-\tSet(uintptr);\n+\tGet()\tuintptr;\t// Get the underlying uintptr.
+\tSet(uintptr);\t// Set the underlying uintptr.
}
type uintptrValueStruct struct {
@@ -343,10 +375,11 @@ func (v *uintptrValueStruct) Set(i uintptr) {
// -- Float
+// FloatValue represents a float value.
type FloatValue interface {
Value;
-\tGet()\tfloat;\n-\tSet(float);\n+\tGet()\tfloat;\t// Get the underlying float.
+\tSet(float);\t// Get the underlying float.
}
type floatValueStruct struct {
@@ -367,10 +400,11 @@ func (v *floatValueStruct) Set(f float) {
// -- Float32
+// Float32Value represents a float32 value.
type Float32Value interface {
Value;
-\tGet()\tfloat32;\n-\tSet(float32);\n+\tGet()\tfloat32;\t// Get the underlying float32.
+\tSet(float32);\t// Get the underlying float32.
}
type float32ValueStruct struct {
@@ -391,10 +425,11 @@ func (v *float32ValueStruct) Set(f float32) {
// -- Float64
+// Float64Value represents a float64 value.
type Float64Value interface {
Value;
-\tGet()\tfloat64;\n-\tSet(float64);\n+\tGet()\tfloat64;\t// Get the underlying float64.
+\tSet(float64);\t// Get the underlying float64.
}
type float64ValueStruct struct {
@@ -415,10 +450,11 @@ func (v *float64ValueStruct) Set(f float64) {
// -- Float80
+// Float80Value represents a float80 value.
type Float80Value interface {
Value;
-\tGet()\tfloat80;\n-\tSet(float80);\n+\tGet()\tfloat80;\t// Get the underlying float80.
+\tSet(float80);\t// Get the underlying float80.
}
type float80ValueStruct struct {
@@ -442,10 +478,11 @@ func (v *Float80ValueStruct) Set(f float80) {
// -- String
+// StringValue represents a string value.
type StringValue interface {
Value;
-\tGet()\tstring;\n-\tSet(string);\n+\tGet()\tstring;\t// Get the underlying string value.
+\tSet(string);\t// Set the underlying string value.
}
type stringValueStruct struct {
@@ -466,10 +493,11 @@ func (v *stringValueStruct) Set(s string) {
// -- Bool
+// BoolValue represents a bool value.
type BoolValue interface {
Value;
-\tGet()\tbool;\n-\tSet(bool);\n+\tGet()\tbool;\t// Get the underlying bool value.
+\tSet(bool);\t// Set the underlying bool value.
}
type boolValueStruct struct {
@@ -490,11 +528,12 @@ func (v *boolValueStruct) Set(b bool) {
// -- Pointer
+// PtrValue represents a pointer value.
type PtrValue interface {
Value;
-\tSub()\tValue;\n-\tGet()\tAddr;\n-\tSetSub(Value);\n+\tSub()\tValue;\t// The Value pointed to.
+\tGet()\tAddr;\t// Get the address stored in the pointer.
+\tSetSub(Value);\t// Set the the pointed-to Value.
}
type ptrValueStruct struct {
@@ -526,15 +565,16 @@ func ptrCreator(typ Type, addr Addr) Value {
// -- Array
// Slices and arrays are represented by the same interface.\n \n+// ArrayValue represents an array or slice value.
type ArrayValue interface {
Value;
-\tIsSlice()\tbool;\n-\tLen()\tint;\n-\tCap() int;\n-\tElem(i int)\tValue;\n-\tSetLen(len int);\n-\tSet(src ArrayValue);\n-\tCopyFrom(src ArrayValue, n int)\n+\tIsSlice()\tbool;\t// Is this a slice (true) or array (false)?
+\tLen()\tint;\t// The length of the array/slice.
+\tCap() int;\t// The capacity of the array/slice (==Len() for arrays).
+\tElem(i int)\tValue;\t// The Value of the i'th element.
+\tSetLen(len int);\t// Set the length; slice only.
+\tSet(src ArrayValue);\t// Set the underlying Value; slice only for src and dest both.
+\tCopyFrom(src ArrayValue, n int)\t// Copy the elements from src; lengths must match.
}
func copyArray(dst ArrayValue, src ArrayValue, n int);\n@@ -581,7 +621,7 @@ func (v *sliceValueStruct) SetLen(len int) {
func (v *sliceValueStruct) Set(src ArrayValue) {
if !src.IsSlice() {
-\t\tpanic("can't set from fixed array");
+\t\tpanic("can't set slice from array");
}
s := src.(*sliceValueStruct);\n if !equalType(v.typ, s.typ) {
@@ -619,10 +659,11 @@ func (v *arrayValueStruct) Cap() int {
}\n \n func (v *arrayValueStruct) SetLen(len int) {
+\tpanicln("can't set len of array");
}\n \n func (v *arrayValueStruct) Set(src ArrayValue) {
-\tpanicln("can't set fixed array");
+\tpanicln("can't set array");
}\n \n func (v *arrayValueStruct) Elem(i int) Value {
@@ -659,10 +700,12 @@ func arrayCreator(typ Type, addr Addr) Value {
// -- Map TODO: finish and test
+// MapValue represents a map value.
+// Its implementation is incomplete.
type MapValue interface {
Value;
-\tLen()\tint;\n-\tElem(key Value)\tValue;\n+\tLen()\tint;\t// The number of elements; currently always returns 0.
+\tElem(key Value)\tValue;\t// The value indexed by key; unimplemented.
}
type mapValueStruct struct {
@@ -684,6 +727,8 @@ func (v *mapValueStruct) Elem(key Value) Value {
// -- Chan
+// ChanValue represents a chan value.
+// Its implementation is incomplete.
type ChanValue interface {
Value;
}
@@ -698,10 +743,11 @@ func chanCreator(typ Type, addr Addr) Value {
// -- Struct
+// StructValue represents a struct value.
type StructValue interface {
Value;
-\tLen()\tint;\n-\tField(i int)\tValue;\n+\tLen()\tint;\t// The number of fields.
+\tField(i int)\tValue;\t// The Value of field i.
}
type structValueStruct struct {
@@ -732,9 +778,10 @@ func structCreator(typ Type, addr Addr) Value {
// -- Interface
+// InterfaceValue represents an interface value.
type InterfaceValue interface {
Value;
-\tGet()\tinterface {};\n+\tGet()\tinterface {};\t// Get the underlying interface{} value.
}
type interfaceValueStruct struct {
@@ -751,6 +798,9 @@ func interfaceCreator(typ Type, addr Addr) Value {
// -- Func
+\n+// FuncValue represents a func value.
+// Its implementation is incomplete.
type FuncValue interface {
Value;
}
@@ -801,6 +851,7 @@ func newValueAddr(typ Type, addr Addr) Value {
return c(typ, addr);\n }\n \n+// NewInitValue creates a new, zero-initialized Value for the specified Type.\n func NewInitValue(typ Type) Value {\n \t// Some values cannot be made this way.\n \tswitch typ.Kind() {\n@@ -819,6 +860,8 @@ func NewInitValue(typ Type) Value {\n \treturn newValueAddr(typ, Addr(&data[0]));\n }\n \n+// NewSliceValue creates a new, zero-initialized slice value (ArrayValue) for the specified\n+// slice type (ArrayType), length, and capacity.\n func NewSliceValue(typ ArrayType, len, cap int) ArrayValue {\n \tif !typ.IsSlice() {\n \t\treturn nil\n@@ -869,7 +922,7 @@ func copyArray(dst ArrayValue, src ArrayValue, n int) {\n \t}\n }\n \n-\n+// NewValue creates a new Value from the interface{} object provided.\n func NewValue(e interface {}) Value {\n \tvalue, typestring, indir := sys.Reflect(e);\n \ttyp, ok := typecache[typestring];
コアとなるコードの解説
このコミットの核心は、Go言語の reflect
パッケージのドキュメンテーションを大幅に改善し、一部の公開関数を内部関数にリネームすることで、その意図を明確にした点にあります。
-
TypeToString
とValueToString
の内部化と目的の明確化:- 以前は
reflect.TypeToString
とreflect.ValueToString
として公開されていた関数が、それぞれreflect.typeToString
とreflect.valueToString
に変更されました。これにより、これらの関数はパッケージの外部から直接アクセスできなくなりました。 - この変更の最も重要な点は、これらの関数が「デバッグ目的のみ」であり、Goの型や値を一般的な文字列に変換するためのものではないという意図が、コードコメントによって明確にされたことです。これは、Goの
reflect
パッケージが提供する文字列表現が、内部的な詳細に依存しており、安定したAPIとして提供されるべきではないという設計判断を反映しています。 - テストコード (
src/lib/reflect/all_test.go
) も、この変更に合わせて更新され、新しい内部関数名を使用するように修正されています。
- 以前は
-
reflect
パッケージ全体の包括的なドキュメンテーションの追加:src/lib/reflect/type.go
のパッケージコメントが拡張され、reflect
パッケージが「コンパイル時に型が不明な型や値を分析するために使用できる」という、その主要な目的が明確に記述されました。これは、Goのインターフェース値の動的な内容を抽出することで実現されると説明されています。Type
インターフェース、およびPtrType
,ArrayType
,MapType
,ChanType
,StructType
,InterfaceType
,FuncType
といった具体的な型を表すインターフェースの定義に、それぞれの役割、メソッドの目的、および返される値に関する詳細なコメントが追加されました。例えば、Type.Kind()
は「記述されるものの種類」を、PtrType.Sub()
は「ポインタが指すアイテムの型」を返すと説明されています。src/lib/reflect/value.go
では、Value
インターフェースとそのメソッド、およびIntValue
,BoolValue
,PtrValue
,ArrayValue
など、各Kind
に対応するValue
インターフェースとそのメソッドに、同様に詳細なコメントが追加されました。これにより、各Value
インターフェースがどのような種類の値を抽象化し、どのような操作(値の取得、設定、要素へのアクセスなど)を提供するかを明確にしています。NewInitValue
,NewSliceValue
,NewValue
といった、reflect.Value
オブジェクトを生成するための関数にも、その目的と引数の意味を説明するコメントが追加されました。
これらの変更は、Go言語の reflect
パッケージの初期段階において、その複雑な機能セットを開発者がより容易に理解し、適切に利用できるようにするための重要なステップでした。特に、デバッグ用途の関数を明確に区別し、APIの意図を詳細なコメントで伝えることで、誤用を防ぎ、パッケージの安定性と保守性を向上させています。
関連リンク
- Go言語の
reflect
パッケージ公式ドキュメント: https://pkg.go.dev/reflect - Go言語の
reflect
パッケージに関するブログ記事 (Go公式ブログ): https://go.dev/blog/laws-of-reflection
参考にした情報源リンク
- Go言語の
reflect
パッケージのソースコード (Goリポジトリ): https://github.com/golang/go/tree/master/src/reflect - Go言語のコメント規約に関する情報: https://go.dev/doc/effective_go#commentary
- Go言語の
reflect
パッケージの歴史的な変更履歴 (GitHub): https://github.com/golang/go/commits/master/src/reflect - Go言語の
reflect
パッケージに関する議論 (Go Issues/Mailing List): 関連する議論は、Goの初期開発におけるリフレクションの設計思想や、ドキュメンテーションの必要性についての手がかりを提供します。 - Go言語の
reflect
パッケージの設計に関する情報 (Go Design Documents): Goの設計ドキュメントは、リフレクションの設計上の決定と、その背後にある理由を理解するのに役立ちます。