[インデックス 1547] ファイルの概要
このコミットは、Go言語のreflect
パッケージにおける「オープン配列 (open array)」という概念を廃止し、代わりに「スライス (slice)」と「配列 (array)」という用語に統一する大規模なリファクタリングです。これにより、Go言語の型システムとランタイムにおける動的な配列のような構造の表現が、より一貫性のあるものになります。
コミット
commit 9a7332fb5bb3287efa5420756c1b79f43937cc14
Author: Rob Pike <r@golang.org>
Date: Fri Jan 23 15:56:04 2009 -0800
remove the "open" concept from reflect and go with slices and arrays.
the two still share an interface and Kind; that's probably ok but might be worth revisiting.
R=rsc
DELTA=74 (1 added, 8 deleted, 65 changed)
OCL=23416
CL=23418
---
src/lib/json/struct.go | 4 +--
src/lib/reflect/all_test.go | 2 +-\
src/lib/reflect/tostring.go | 2 +-\
src/lib/reflect/type.go | 10 +++---\
src/lib/reflect/value.go | 87 +++++++++++++++++++++------------------------
5 files changed, 49 insertions(+), 56 deletions(-)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9a7332fb5bb3287efa5420756c1b79f43937cc14
元コミット内容
reflect
パッケージから「open」という概念を削除し、スライスと配列に移行する。
この2つはまだインターフェースとKindを共有しているが、それはおそらく問題ないだろうが、再検討する価値があるかもしれない。
変更の背景
Go言語の初期開発段階において、動的なサイズを持つ配列のような構造を内部的に「オープン配列 (open array)」と呼んでいた時期があったようです。しかし、Go言語の設計が進むにつれて、この概念はより明確な「スライス (slice)」という形で外部に公開され、言語の主要な機能として定着しました。
このコミットは、reflect
パッケージ(Goのランタイム型情報を検査・操作するためのパッケージ)内で使われていた古い「オープン配列」という内部的な用語や構造を、Go言語の外部インターフェースで一般的に使われる「スライス」という用語に統一することを目的としています。これにより、reflect
パッケージのコードベースが、Go言語全体の用語体系と整合性が取れ、理解しやすくなります。また、将来的なGo言語の進化や最適化においても、用語の混乱を避けることができます。
前提知識の解説
Go言語のreflect
パッケージ
reflect
パッケージは、Goプログラムが実行時に自身の構造(型、値、メソッドなど)を検査・操作するための機能を提供します。これにより、ジェネリックなデータ処理、シリアライゼーション/デシリアライゼーション、ORM(Object-Relational Mapping)などの高度な機能を実現できます。reflect
パッケージは、Goの型システムを抽象化し、Type
インターフェース(型の情報)とValue
インターフェース(値の操作)を通じて、プログラムのメタデータを扱います。
Go言語の配列とスライス
-
配列 (Array): Goの配列は、固定長で型付けされた要素のシーケンスです。配列の長さは型の一部であり、コンパイル時に決定されます。例えば、
[5]int
と[10]int
は異なる型として扱われます。配列は値型であり、代入や関数への引数として渡される際には、配列全体のコピーが作成されます。 -
スライス (Slice): スライスは、Goにおいて最も一般的に使用される動的なシーケンス型です。スライスは、基となる配列の一部を参照する構造であり、長さ(
len
)と容量(cap
)を持ちます。スライスは参照型であり、append
関数などによって動的にサイズを変更できます。スライスは、内部的にはポインタ、長さ、容量の3つの要素から構成されます。この「動的なサイズ変更が可能」という特性が、かつて「オープン配列」と呼ばれていた概念に相当します。
reflect
パッケージにおける配列とスライスの表現
reflect
パッケージでは、配列とスライスはどちらもArray
というKind(型カテゴリ)に分類されます。しかし、その振る舞い(固定長か動的か)は異なります。このコミット以前は、この動的な特性を「オープン (Open)」という概念で区別していましたが、このコミット以降は「スライス (IsSlice)」というより直感的な用語で区別するようになります。
技術的詳細
このコミットの主要な変更点は、reflect
パッケージ内の「オープン配列」に関連するすべての用語、型、メソッド名を「スライス」に関連するものに置き換えることです。
具体的には、以下の変更が行われています。
-
用語の統一:
Open()
メソッドがIsSlice()
メソッドに改名されました。これは、その型がスライスであるかどうかを判定する意図を明確にします。- 構造体フィールド名
open
がisslice
に改名されました。 runtimeArray
という内部構造体名がruntimeSlice
に改名されました。これは、スライスのランタイム表現をより正確に反映しています。openArrayValueStruct
という構造体名がsliceValueStruct
に改名されました。これは、reflect.Value
がスライスを表す際の内部構造体です。fixedArrayValueStruct
という構造体名がarrayValueStruct
に改名されました。これは、reflect.Value
が固定長配列を表す際の内部構造体です。
-
ファクトリ関数の変更:
reflect.NewOpenArrayValue
という関数がreflect.NewSliceValue
に改名されました。この関数は、新しいスライス値を生成するために使用されます。
-
コード内の参照の更新:
src/lib/json/struct.go
やsrc/lib/reflect/all_test.go
など、既存のコードベースでNewOpenArrayValue
やOpen()
メソッドを使用していた箇所が、それぞれNewSliceValue
やIsSlice()
に更新されました。
これらの変更により、reflect
パッケージのAPIと内部実装が、Go言語の標準的な用語である「スライス」に完全に準拠するようになります。これにより、reflect
パッケージを扱う開発者にとって、より直感的で理解しやすいコードベースが提供されます。
コアとなるコードの変更箇所
このコミットは、主にsrc/lib/reflect/type.go
とsrc/lib/reflect/value.go
の2つのファイルに大きな変更を加えています。
src/lib/reflect/type.go
ArrayType
インターフェースの定義が変更され、Open() bool
メソッドがIsSlice() bool
に置き換えられました。--- a/src/lib/reflect/type.go +++ b/src/lib/reflect/type.go @@ -165,7 +165,7 @@ func (t *ptrTypeStruct) Sub() Type { // -- Array type ArrayType interface { - Open() bool; + IsSlice() bool; Len() int; Elem() Type; } @@ -173,7 +173,7 @@ type ArrayType interface { type arrayTypeStruct struct { commonType; elem *stubType; - open bool; // otherwise fixed size + isslice bool; // otherwise fixed array len int; } @@ -182,14 +182,14 @@ func newArrayTypeStruct(name, typestring string, open bool, len int, elem *stubT } func (t *arrayTypeStruct) Size() int { - if t.open { + if t.isslice { return ptrsize*2 // open arrays are 2-word headers } return t.len * t.elem.Get().Size(); } -func (t *arrayTypeStruct) Open() bool { - return t.open +func (t *arrayTypeStruct) IsSlice() bool { + return t.isslice } func (t *arrayTypeStruct) Len() int {
src/lib/reflect/value.go
-
ArrayValue
インターフェースの定義が変更され、Open() bool
メソッドがIsSlice() bool
に置き換えられました。 -
runtimeArray
構造体がruntimeSlice
に、openArrayValueStruct
構造体がsliceValueStruct
に、fixedArrayValueStruct
構造体がarrayValueStruct
にそれぞれ改名されました。 -
これらの構造体内のフィールド名やメソッド名も、新しい命名規則に合わせて変更されました。
-
NewOpenArrayValue
関数がNewSliceValue
に改名されました。--- a/src/lib/reflect/value.go +++ b/src/lib/reflect/value.go @@ -544,11 +544,12 @@ func ptrCreator(typ Type, addr Addr) Value { } // -- Array +// Slices and arrays are represented by the same interface. type ArrayValue interface { Kind() int; Type() Type; - Open() bool; + IsSlice() bool; Len() int; Cap() int; Elem(i int) Value; @@ -560,114 +561,114 @@ func copyArray(dst ArrayValue, src ArrayValue, n int); /* -- Run-time representation of open arrays looks like this: -- struct Array { -+ Run-time representation of slices looks like this: -+ struct Slice { \t\t\tbyte*\tarray;\t\t// actual data \t\t\tuint32\tnel;\t\t// number of elements \t\t\tuint32\tcap;\ \t\t}; */ -type runtimeArray struct { +type runtimeSlice struct { data Addr; len uint32; cap uint32; } -type openArrayValueStruct struct { +type sliceValueStruct struct { commonValue; elemtype Type; elemsize int; - array *runtimeArray; + slice *runtimeSlice; } -func (v *openArrayValueStruct) Open() bool { +func (v *sliceValueStruct) IsSlice() bool { return true } -func (v *openArrayValueStruct) Len() int { - return int(v.array.len); +func (v *sliceValueStruct) Len() int { + return int(v.slice.len); } -func (v *openArrayValueStruct) Cap() int { - return int(v.array.cap); +func (v *sliceValueStruct) Cap() int { + return int(v.slice.cap); } -func (v *openArrayValueStruct) SetLen(len int) { +func (v *sliceValueStruct) SetLen(len int) { if len > v.Cap() { - panicln("reflect: OpenArrayValueStruct.SetLen", len, v.Cap()); + panicln("reflect: sliceValueStruct.SetLen", len, v.Cap()); } - v.array.len = uint32(len); + v.slice.len = uint32(len); } -func (v *openArrayValueStruct) Set(src ArrayValue) { - if !src.Open() { +func (v *sliceValueStruct) Set(src ArrayValue) { + if !src.IsSlice() { panic("can't set from fixed array"); } - s := src.(*openArrayValueStruct); + s := src.(*sliceValueStruct); if !equalType(v.typ, s.typ) { - panicln("incompatible array types in ArrayValue.Set()"); + panicln("incompatible types in ArrayValue.Set()"); } - *v.array = *s.array; + *v.slice = *s.slice; } -func (v *openArrayValueStruct) Elem(i int) Value { - data_uint := uintptr(v.array.data) + uintptr(i * v.elemsize); +func (v *sliceValueStruct) Elem(i int) Value { + data_uint := uintptr(v.slice.data) + uintptr(i * v.elemsize); return newValueAddr(v.elemtype, Addr(data_uint)); } -func (v *openArrayValueStruct) CopyFrom(src ArrayValue, n int) { +func (v *sliceValueStruct) CopyFrom(src ArrayValue, n int) { copyArray(v, src, n); } -type fixedArrayValueStruct struct { +type arrayValueStruct struct { commonValue; elemtype Type; elemsize int; len int; } -func (v *fixedArrayValueStruct) Open() bool { +func (v *arrayValueStruct) IsSlice() bool { return false } -func (v *fixedArrayValueStruct) Len() int { +func (v *arrayValueStruct) Len() int { return v.len } -func (v *fixedArrayValueStruct) Cap() int { +func (v *arrayValueStruct) Cap() int { return v.len } -func (v *fixedArrayValueStruct) SetLen(len int) { +func (v *arrayValueStruct) SetLen(len int) { } -func (v *fixedArrayValueStruct) Set(src ArrayValue) { +func (v *arrayValueStruct) Set(src ArrayValue) { panicln("can't set fixed array"); } -func (v *fixedArrayValueStruct) Elem(i int) Value { +func (v *arrayValueStruct) Elem(i int) Value { data_uint := uintptr(v.addr) + uintptr(i * v.elemsize); return newValueAddr(v.elemtype, Addr(data_uint)); return nil } -func (v *fixedArrayValueStruct) CopyFrom(src ArrayValue, n int) { +func (v *arrayValueStruct) CopyFrom(src ArrayValue, n int) { copyArray(v, src, n); } func arrayCreator(typ Type, addr Addr) Value { arraytype := typ.(ArrayType); - if arraytype.Open() { - v := new(openArrayValueStruct); + if arraytype.IsSlice() { + v := new(sliceValueStruct); v.kind = ArrayKind; v.addr = addr; v.typ = typ; v.elemtype = arraytype.Elem(); v.elemsize = v.elemtype.Size(); - v.array = addr.(*runtimeArray); + v.slice = addr.(*runtimeSlice); return v; } - v := new(fixedArrayValueStruct); + v := new(arrayValueStruct); v.kind = ArrayKind; v.addr = addr; v.typ = typ; @@ -832,7 +833,7 @@ func NewInitValue(typ Type) Value { case FuncKind: // must be pointers, at least for now (TODO?) return nil; case ArrayKind: - if typ.(ArrayType).Open() { + if typ.(ArrayType).IsSlice() { return nil } } @@ -844,20 +845,12 @@ func NewInitValue(typ Type) Value { return newValueAddr(typ, Addr(&data[0])); } -/* -- Run-time representation of open arrays looks like this: -- struct Array { -- byte* array; // actual data -- uint32 nel; // number of elements -- uint32 cap; // allocated number of elements -- }; -*/ -func NewOpenArrayValue(typ ArrayType, len, cap int) ArrayValue { - if !typ.Open() { +func NewSliceValue(typ ArrayType, len, cap int) ArrayValue { + if !typ.IsSlice() { return nil } - array := new(runtimeArray); + array := new(runtimeSlice); size := typ.Elem().Size() * cap; if size == 0 { size = 1; @@ -870,7 +863,7 @@ func NewOpenArrayValue(typ ArrayType, len, cap int) ArrayValue { return newValueAddr(typ, Addr(array)); } -// Works on both fixed and open arrays. +// Works on both slices and arrays func copyArray(dst ArrayValue, src ArrayValue, n int) { if n == 0 { return
コアとなるコードの解説
このコミットの核心は、Go言語のreflect
パッケージが、Go言語のユーザーが日常的に使用する「スライス」という概念と、その内部表現をより密接に連携させるように変更された点にあります。
-
ArrayType
インターフェースの変更:Open()
メソッドがIsSlice()
に変更されたことで、型の特性を問い合わせる際に「これはオープン配列か?」ではなく「これはスライスか?」という、より現代のGo言語に即した問いかけができるようになりました。これは、reflect
パッケージの利用者が、Go言語の他の部分で慣れ親しんだ用語で型情報を扱えるようになることを意味します。 -
内部構造体とファクトリ関数のリネーム:
runtimeArray
からruntimeSlice
へ、openArrayValueStruct
からsliceValueStruct
へのリネームは、Goのランタイムがスライスをどのように表現しているかを、コードの命名からも明確に示しています。同様に、NewOpenArrayValue
からNewSliceValue
への変更は、スライスを生成するためのファクトリ関数が、その目的をより直接的に表現するようになりました。
これらの変更は、単なる命名の変更にとどまらず、reflect
パッケージの設計思想が、Go言語の進化に合わせてより洗練されたことを示しています。これにより、reflect
パッケージは、Go言語の型システムとランタイムの振る舞いを、より正確かつ直感的に反映するようになりました。
関連リンク
- Go言語の
reflect
パッケージのドキュメント: https://pkg.go.dev/reflect - Go言語の配列とスライスに関する公式ブログ記事 (Go Slices: usage and internals): https://go.dev/blog/slices-intro
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Web検索: "Go language open array concept" (Go言語には「オープン配列」という明示的な概念はなく、スライスがその役割を果たすという情報)