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

[インデックス 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パッケージ内の「オープン配列」に関連するすべての用語、型、メソッド名を「スライス」に関連するものに置き換えることです。

具体的には、以下の変更が行われています。

  1. 用語の統一:

    • Open()メソッドがIsSlice()メソッドに改名されました。これは、その型がスライスであるかどうかを判定する意図を明確にします。
    • 構造体フィールド名openissliceに改名されました。
    • runtimeArrayという内部構造体名がruntimeSliceに改名されました。これは、スライスのランタイム表現をより正確に反映しています。
    • openArrayValueStructという構造体名がsliceValueStructに改名されました。これは、reflect.Valueがスライスを表す際の内部構造体です。
    • fixedArrayValueStructという構造体名がarrayValueStructに改名されました。これは、reflect.Valueが固定長配列を表す際の内部構造体です。
  2. ファクトリ関数の変更:

    • reflect.NewOpenArrayValueという関数がreflect.NewSliceValueに改名されました。この関数は、新しいスライス値を生成するために使用されます。
  3. コード内の参照の更新:

    • src/lib/json/struct.gosrc/lib/reflect/all_test.goなど、既存のコードベースでNewOpenArrayValueOpen()メソッドを使用していた箇所が、それぞれNewSliceValueIsSlice()に更新されました。

これらの変更により、reflectパッケージのAPIと内部実装が、Go言語の標準的な用語である「スライス」に完全に準拠するようになります。これにより、reflectパッケージを扱う開発者にとって、より直感的で理解しやすいコードベースが提供されます。

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

このコミットは、主にsrc/lib/reflect/type.gosrc/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言語の公式ドキュメント
  • Go言語のソースコード
  • Web検索: "Go language open array concept" (Go言語には「オープン配列」という明示的な概念はなく、スライスがその役割を果たすという情報)