[インデックス 16690] ファイルの概要
このコミットは、Go言語のreflect
パッケージに新たなメソッドValue.Slice3
とValue.SetCap
を追加し、既存のValue.Slice
メソッドを更新するものです。これにより、Go 1.2で導入された3インデックススライス構文x[i:j:k]
(cap
を指定するスライス操作)をreflect
パッケージ経由でサポートします。
変更されたファイルは以下の通りです。
src/pkg/reflect/all_test.go
: 68行追加src/pkg/reflect/value.go
: 85行追加、12行削除
合計で141行の追加と12行の削除が行われています。
コミット
commit 4d8aefde470de630a1f6f6fc2c481fedb4a293c8
Author: Russ Cox <rsc@golang.org>
Date: Mon Jul 1 20:32:53 2013 -0400
reflect: add Value.Slice3 and Value.SetCap methods, to match x[i:j:k]
Design doc at golang.org/s/go12slice.
R=golang-dev, r, nightlyone
CC=golang-dev
https://golang.org/cl/10761045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4d8aefde470de630a1f6f6fc2c481fedb4a293c8
元コミット内容
reflect: add Value.Slice3 and Value.SetCap methods, to match x[i:j:k]
Design doc at golang.org/s/go12slice.
R=golang-dev, r, nightlyone
CC=golang-dev
https://golang.org/cl/10761045
変更の背景
このコミットの主な背景は、Go 1.2で導入された新しいスライス構文x[i:j:k]
(3インデックススライス)をreflect
パッケージでサポートすることです。従来の2インデックススライスx[i:j]
では、結果として得られるスライスの長さ(len
)と容量(cap
)は、元のスライスの容量から自動的に決定されていました。しかし、3インデックススライスでは、k
を指定することで、結果スライスの容量を明示的に制御できるようになります。
reflect
パッケージは、Goプログラムが自身の構造を検査し、実行時に値を操作するための機能を提供します。このパッケージが言語の新しい機能(この場合は3インデックススライス)をサポートしない場合、リフレクションを使用するプログラムは、その新しい機能の恩恵を受けることができません。したがって、reflect
パッケージにValue.Slice3
メソッドを追加し、3インデックススライス操作をリフレクション経由で実行できるようにすることが必要でした。
また、スライスの容量を直接設定するためのValue.SetCap
メソッドも追加されています。これは、リフレクションを通じてスライスの容量を動的に変更するシナリオに対応するためです。これらの変更は、Go言語の柔軟性とリフレクション機能の完全性を高めることを目的としています。
前提知識の解説
Goのスライス(Slice)
Go言語のスライスは、配列をラップした動的なビューであり、Goプログラミングにおいて非常に重要なデータ構造です。スライスは、基になる配列の一部を参照し、その長さ(len
)と容量(cap
)を持ちます。
- 長さ(
len
): スライスに含まれる要素の数。 - 容量(
cap
): スライスの最初の要素から、基になる配列の末尾までの要素の数。これは、スライスが再割り当てなしで拡張できる最大サイズを示します。
スライスは、内部的には「スライスヘッダ」と呼ばれる小さな構造体で表現されます。このヘッダは以下の3つの要素から構成されます。
- データポインタ(Data Pointer): 基になる配列の最初の要素へのポインタ。
- 長さ(Length): スライスの現在の長さ。
- 容量(Capacity): スライスの容量。
スライスを別の変数に代入したり、関数に引数として渡したりすると、このスライスヘッダがコピーされます。しかし、コピーされたヘッダも元のヘッダと同じ基になる配列を指しているため、一方のスライスを通じて基になる配列の要素を変更すると、もう一方のスライスからもその変更が見えます。
スライス操作 x[i:j]
と x[i:j:k]
-
2インデックススライス
x[i:j]
:i
: スライスの開始インデックス(inclusive)。j
: スライスの終了インデックス(exclusive)。- 結果のスライスの長さは
j - i
となります。 - 結果のスライスの容量は、元のスライスの容量から
i
を引いた値となります。つまり、cap(x[i:j]) == cap(x) - i
です。
-
3インデックススライス
x[i:j:k]
:i
: スライスの開始インデックス(inclusive)。j
: スライスの終了インデックス(exclusive)。k
: 結果のスライスの容量の終了インデックス(exclusive)。- 結果のスライスの長さは
j - i
となります。 - 結果のスライスの容量は
k - i
となります。 - この構文は、特に
append
操作などで、基になる配列の再割り当てを避けるために、より小さい容量を持つスライスを作成したい場合に有用です。例えば、make([]T, length, capacity)
でスライスを作成するのと同様に、既存のスライスから特定の容量を持つ新しいスライスを作成できます。
reflect
パッケージ
reflect
パッケージは、Goプログラムが実行時に型情報(Type
)と値情報(Value
)を操作するための機能を提供します。これにより、ジェネリックなプログラミングや、構造体のフィールドへのアクセス、メソッドの呼び出しなどを動的に行うことができます。
reflect.Value
: Goの任意の値を表す構造体です。この構造体を通じて、値の型、内容、および操作(例:フィールドの設定、メソッドの呼び出し)を行うことができます。reflect.Kind
: Goの組み込み型(例:Int
,String
,Slice
,Array
など)を表す列挙型です。reflect.ValueOf(i interface{}) Value
: 任意のGoの値をreflect.Value
型に変換します。reflect.Value.Interface() interface{}
:reflect.Value
を元のGoのインターフェース型に戻します。
unsafe
パッケージ
unsafe
パッケージは、Goの型安全性をバイパスする低レベルな操作を可能にします。これには、ポインタとuintptr
間の変換や、任意の型へのポインタのキャストなどが含まれます。reflect
パッケージのような低レベルな操作を行うライブラリでは、パフォーマンスや特定の機能を実現するためにunsafe
パッケージが使用されることがあります。
このコミットでは、スライスヘッダ(SliceHeader
)を直接操作するためにunsafe.Pointer
が使用されています。SliceHeader
は、スライスの内部表現(データポインタ、長さ、容量)を定義する構造体で、reflect
パッケージがスライスを操作する際に利用されます。
技術的詳細
このコミットは、reflect
パッケージのValue
型に、Go 1.2で導入された3インデックススライス構文x[i:j:k]
に対応するSlice3
メソッドと、スライスの容量を直接設定するSetCap
メソッドを追加します。また、既存のSlice
メソッドも、引数名をbeg, end
からi, j
に変更し、より一般的なスライス操作の表記に合わせるように修正されています。
Value.SetCap(n int)
メソッド
このメソッドは、reflect.Value
が表すスライスの容量をn
に設定します。
- 前提条件:
v
のKind
がSlice
である必要があります。それ以外の場合、パニックが発生します。v
が変更可能(assignable)である必要があります。
- パニック条件:
n
がスライスの現在の長さ(len
)より小さい場合。n
がスライスの現在の容量(cap
)より大きい場合。- これらの条件は、スライスの整合性を保つために重要です。容量は常に長さ以上であり、既存の容量を超えることはできません(その場合は再割り当てが必要となり、それは
SetCap
の役割ではありません)。
- 実装:
- 内部的には、
v.val
(reflect.Value
が保持する値へのポインタ)を*SliceHeader
型にキャストし、そのCap
フィールドを直接n
に設定します。これはunsafe
パッケージを利用して行われます。
- 内部的には、
Value.Slice3(i, j, k int)
メソッド
このメソッドは、reflect.Value
が表す配列またはスライスに対して、3インデックススライス操作v[i:j:k]
を実行し、結果として新しいreflect.Value
(スライス)を返します。
- 前提条件:
v
のKind
がArray
またはSlice
である必要があります。それ以外の場合、パニックが発生します。v
が配列の場合、アドレス可能(addressable)である必要があります。
- パニック条件:
i
,j
,k
のインデックスが範囲外の場合。具体的には、0 <= i <= j <= k <= cap
の条件が満たされない場合。
- 実装:
v
のKind
に応じて、基になるデータポインタ(base
)、元の容量(cap
)、および要素の型情報(typ
)を取得します。- 新しいスライスヘッダを構築し、
Data
ポインタをbase + i * elemSize
に、Len
をj - i
に、Cap
をk - i
に設定します。 - この操作も
unsafe.Pointer
を使用して、スライスヘッダを直接操作します。 - 結果として得られる新しいスライスを表す
reflect.Value
を返します。
Value.Slice(i, j int)
メソッドの変更
既存のValue.Slice
メソッドは、2インデックススライス操作v[i:j]
を実行します。このコミットでは、引数名がbeg, end
からi, j
に変更され、より一般的なスライス表記に統一されました。機能的な変更はありませんが、コードの可読性と一貫性が向上しています。
これらの変更により、reflect
パッケージはGo言語の最新のスライス構文を完全にサポートし、リフレクションを利用するプログラムがより柔軟にスライスを操作できるようになります。
コアとなるコードの変更箇所
src/pkg/reflect/all_test.go
TestSlice3
関数が追加されました。このテストは、Value.Slice3
メソッドの動作を検証します。- スライスと配列の両方に対して
Slice3
を呼び出し、結果のlen
とcap
が期待通りであるか、および内容が正しいかを確認します。 - 不正なインデックスが指定された場合にパニックが発生することを確認する
shouldPanic
テストも含まれています。 - 文字列に対して
Slice3
を呼び出すとパニックが発生することもテストされています(文字列は3インデックススライスをサポートしないため)。
- スライスと配列の両方に対して
TestSetLenCap
関数が追加されました。このテストは、Value.SetLen
と新しく追加されたValue.SetCap
メソッドの動作を検証します。- スライスに対して
SetLen
とSetCap
を呼び出し、len
とcap
が正しく更新されることを確認します。 - 不正な値(例:
len
より小さいcap
、cap
より大きいlen
)が指定された場合にパニックが発生することを確認するshouldPanic
テストも含まれています。 - 配列に対して
SetLen
やSetCap
を呼び出すとパニックが発生することもテストされています(配列は固定長のため)。
- スライスに対して
src/pkg/reflect/value.go
Value.SetCap(n int)
メソッドが追加されました。v.mustBeAssignable()
とv.mustBe(Slice)
で、Value
がスライスであり、かつ変更可能であることを確認します。n
が現在の長さより小さいか、現在の容量より大きい場合にパニックを発生させます。*SliceHeader
にキャストしてs.Cap = n
で容量を設定します。
Value.Slice(i, j int)
メソッドの引数名がbeg, end
からi, j
に変更されました。- 内部のインデックスチェックも
i
,j
を使用するように更新されました。 - 文字列スライスの場合のインデックスチェックも
i
,j
を使用するように更新されました。
- 内部のインデックスチェックも
Value.Slice3(i, j, k int)
メソッドが追加されました。v.kind()
に基づいて、Array
またはSlice
型であることを確認し、それ以外の場合はパニックを発生させます。- 配列の場合、
v.flag&flagAddr == 0
でアドレス可能であることを確認します。 - インデックス
i, j, k
が0 <= i <= j <= k <= cap
の範囲内にあることを確認し、範囲外の場合はパニックを発生させます。 - 新しいスライスヘッダを構築し、
Data
,Len
,Cap
フィールドを計算された値で設定します。 Value{typ.common(), unsafe.Pointer(&x), fl}
で新しいreflect.Value
を構築して返します。
コアとなるコードの解説
Value.SetCap
の実装 (src/pkg/reflect/value.go
)
// SetCap sets v's capacity to n.
// It panics if v's Kind is not Slice or if n is smaller than the length or
// greater than the capacity of the slice.
func (v Value) SetCap(n int) {
v.mustBeAssignable() // Valueが変更可能であることを確認
v.mustBe(Slice) // Valueがスライス型であることを確認
s := (*SliceHeader)(v.val) // Valueの内部ポインタをSliceHeaderにキャスト
if n < int(s.Len) || n > int(s.Cap) { // 新しい容量nが現在の長さより小さいか、現在の容量より大きい場合
panic("reflect: slice capacity out of range in SetCap") // パニック
}
s.Cap = n // SliceHeaderのCapフィールドをnに設定
}
このコードは、reflect.Value
が表すスライスの容量を直接変更します。v.val
はreflect.Value
の内部的なフィールドで、Goの実際の値へのポインタを保持しています。これを*SliceHeader
にキャストすることで、スライスの内部構造(データポインタ、長さ、容量)に直接アクセスし、Cap
フィールドを更新しています。パニック条件は、スライスの整合性を保つために非常に重要です。
Value.Slice3
の実装 (src/pkg/reflect/value.go
)
// Slice3 is the 3-index form of the slice operation: it returns v[i:j:k].
// It panics if v's Kind is not Array or Slice, or if v is an unaddressable array,
// or if the indexes are out of bounds.
func (v Value) Slice3(i, j, k int) Value {
var (
cap int
typ *sliceType
base unsafe.Pointer
)
switch kind := v.kind(); kind {
default:
panic(&ValueError{"reflect.Value.Slice3", kind}) // ArrayまたはSlice以外はパニック
case Array:
if v.flag&flagAddr == 0 {
panic("reflect.Value.Slice: slice of unaddressable array") // アドレス不可能な配列はパニック
}
tt := (*arrayType)(unsafe.Pointer(v.typ)) // 配列の型情報を取得
cap = int(tt.len) // 配列の長さを容量とする
typ = (*sliceType)(unsafe.Pointer(tt.slice)) // 配列の要素型からスライス型を構築
base = v.val // 配列の基底ポインタ
case Slice:
typ = (*sliceType)(unsafe.Pointer(v.typ)) // スライスの型情報を取得
s := (*SliceHeader)(v.val) // スライスヘッダを取得
base = unsafe.Pointer(s.Data) // スライスの基底ポインタ
cap = s.Cap // スライスの容量
}
if i < 0 || j < i || k < j || k > cap { // インデックスの範囲チェック
panic("reflect.Value.Slice3: slice index out of bounds") // 範囲外はパニック
}
// Declare slice so that the garbage collector
// can see the base pointer in it.
var x []unsafe.Pointer // 新しいスライスを宣言(GCが基底ポインタを認識できるように)
// Reinterpret as *SliceHeader to edit.
s := (*SliceHeader)(unsafe.Pointer(&x)) // 新しいスライスのヘッダを操作
s.Data = uintptr(base) + uintptr(i)*typ.elem.Size() // データポインタを設定
s.Len = j - i // 長さを設定
s.Cap = k - i // 容量を設定
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift // フラグを設定
return Value{typ.common(), unsafe.Pointer(&x), fl} // 新しいreflect.Valueを返す
}
この関数は、Goの3インデックススライス操作x[i:j:k]
をリフレクションで実現します。
まず、入力Value
が配列かスライスかを判断し、それぞれのケースで基になるデータポインタ(base
)と元の容量(cap
)を取得します。
次に、i
, j
, k
のインデックスが有効な範囲内にあるかを厳密にチェックします。
そして、unsafe.Pointer
を使って新しいスライスヘッダを構築し、Data
、Len
、Cap
フィールドを計算された値で設定します。s.Data = uintptr(base) + uintptr(i)*typ.elem.Size()
の部分は、基になる配列のi
番目の要素から始まるようにデータポインタを調整しています。
最後に、新しいスライスを表すreflect.Value
を生成して返します。var x []unsafe.Pointer
は、ガベージコレクタが新しいスライスの基底ポインタを正しく追跡できるようにするための重要なステップです。
Value.Slice
の変更 (src/pkg/reflect/value.go
)
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -1531,17 +1544,18 @@ func (v Value) SetString(x string) {
*(*string)(v.val) = x
}
-// Slice returns a slice of v.
-// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array.
-func (v Value) Slice(beg, end int) Value {
+// Slice returns v[i:j].
+// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array,
+// or if the indexes are out of bounds.
+func (v Value) Slice(i, j int) Value {
var (
cap int
typ *sliceType
base unsafe.Pointer
)
- switch k := v.kind(); k {
+ switch kind := v.kind(); kind {
default:
- panic(&ValueError{"reflect.Value.Slice", k})
+ panic(&ValueError{"reflect.Value.Slice", kind})
case Array:
if v.flag&flagAddr == 0 {
@@ -1560,17 +1574,17 @@ func (v Value) Slice(beg, end int) Value {
case String:
s := (*StringHeader)(v.val)
- if beg < 0 || end < beg || end > s.Len {
+ if i < 0 || j < i || j > s.Len {
panic("reflect.Value.Slice: string slice index out of bounds")
}
var x string
val := (*StringHeader)(unsafe.Pointer(&x))
- val.Data = s.Data + uintptr(beg)
- val.Len = end - beg
+ val.Data = s.Data + uintptr(i)
+ val.Len = j - i
return Value{v.typ, unsafe.Pointer(&x), v.flag}
}
- if beg < 0 || end < beg || end > cap {
+ if i < 0 || j < i || j > cap {
panic("reflect.Value.Slice: slice index out of bounds")
}
@@ -1579,9 +1593,56 @@ func (v Value) Slice(beg, end int) Value {
// Reinterpret as *SliceHeader to edit.
s := (*SliceHeader)(unsafe.Pointer(&x))
- s.Data = uintptr(base) + uintptr(beg)*typ.elem.Size()
- s.Len = end - beg
- s.Cap = cap - beg
+ s.Data = uintptr(base) + uintptr(i)*typ.elem.Size()
+ s.Len = j - i
+ s.Cap = cap - i
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
return Value{typ.common(), unsafe.Pointer(&x), fl}
}
この変更は主に引数名の変更と、それに伴う内部の変数名の更新です。機能的なロジックは変わっていませんが、Go言語の慣習に合わせた引数名にすることで、コードの意図がより明確になっています。
関連リンク
- Go 1.2 スライスデザインドキュメント: golang.org/s/go12slice
- Go CL (Change List): https://golang.org/cl/10761045
参考にした情報源リンク
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFl2cK1aT4SQ9P9jbuOcWBVrSatiwrVI6ZldIibRbluWsMBCjjKmS2OSjXx8BTZ3INdCZikauMg7DolZTGt7orfNAN6LPtZzk-EmIiSG9-iGyLXimfI6re2Nd2HjM-ryhOUnA==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG15X7uIj_lO_pdxf3okq5_HhddPd7ub44ztEuCxAEC-WmiM2wJkbkDpkLONG0sZ2RFXquejrGI0IdhqxpaGe0dKmaJOYc-gTEwtkbCr06m6RLdXLhY0MbX04jL99kgOsMX29kgOsMX23o=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF7OyIRHJ9dEusp1ztHzGZJKDLyxVDXzs_yFRH93wk40tYDkdlFEQhApXtu9RIwR324JtzqtWy1FVfe3uAlB4v_JoY_nVdlv7E_lirumuctBe9Ln6WQ5U3VF3LQUCoSa49Iu8Pvjfimjynt3YIiNv2oe9kI06Ipaz9Wwwy
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEdzgOibcyO29m4wtqbFA79ZF8VPwQgkMiQ-vCoYXhkikxNcCD6l6Z6X5MgieXY6PYX7ryQtcWHbsGWP8WZHqltsVMekDx2CLZg4usOurflG8S0WSB-5PEjvGdd
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFwpB7FH2jYoumOGF_kQp_MRYPSPbcueKsCL6J0lrBebgXxjqlxtPz-ZtttlUPOg04EFJ8rLtia-oQVO5w9qbI66BfiJ9ZTSYN1suaYjLHmAxks6R54xbEkIqKJmsuIbts2ypYuuNgmLG-8Y8EISZjlNSfG4cCnSVoZ737KJ4FpKzQBo
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE0zeixT8fXdcbqqPm8wEYo_EQpGzjnPY228ObvCUM7HDUiJacjA2zsKLhufq2w7kNBLwpVS9BboB0UnaUmo25xA36rierRuC444FapOqP5VjkIjKIfhxCfdmdpiTnoVQu5erTI53KoIuXtw0O1WoO79UBFWlyB4jV6TigX
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH872KwpPt1ay2B0QNuAIeDNNHierCiQKOipNGPYsgI2tiqyd7DH_m27xOkx-LgfIf7XrEvXQqYdoa_9K2cQfFhNXhnkbKG34LXAG6KnPY4F5LHaNr_rtBaEVlzlsDcxtAUyDaP8D0t7KioBEb_aPsYZ_L_DzWnTxmu0zwptY71Rg==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEMn2XVfDEGv0KdncobZjRpT2bVd37aqWQse_FHG4bYc8Q8jXjmeoiT0stCvLbFeNiZkR8vOfgNcNY7Ycl0gNISLtQtR1szuWDWh5uJqXbtYac_8eBP5kQQHvMCqcdcHpe6XHbC66rLzZwcnXbBJw-3q_tyldJp6w==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFkoy7Nz12AkNeKLXvzfp_YT9LIHWIRTSTHMj2n89kHHiREyGvZiuqT-LkKEobq3O-8oa0DTXKELxe8hUDw3mgGjAfuQAO4D40YM9F0PcHlmd-oBvVs0nuc8p298r4l40MpPaC6hA3B-hVd-wMZgV3chy6p2zjKsiHL6qo395Ak_OJCzZuPvuW1i65B8M_opcN0XfaB51D1xwSucWiXmrHIkg==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFt5Jahx7yn-nU0d7yTAcgg4Xqa_wF96LYKvG3j82LyuFAA8FzZfMExSqrGZBOj5AIzOxyxVgsvPbJ4ClR0KVe3MkwVWtgmcpKq8ZMGFbo9uQfzTa7uNT5UILuxYgsY37K6DHa4f2E=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGHMV9sEKuwyPxbrYH43EHdiCZXZyP35WTt2NzC9VRF0wZXAqcgL5-P9ZM0RBNB5JdS58zyk2u2kAZKOtFTC_ldTD4RIEMo1ClrhvBp94RmXY4AshceupfkbC9QJX5eEwjSENYXew==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHe0GAh4zBk2BkHMr36wQggcVyrG2R6bPcIpg7-3eT--hXOqFKYY9IO7PiALVCHcdp1Hy7kY2yC8Qk1CzywlVC0apUiX5yX4VReSyqDUmLvh_vkqsNBTt1McmQaYo2SqQxLxcgpvbYeDsMKpoeejCltuR24Oc_wDwWMiG9AuETovhs7Lw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHv1hzM1hy5lSZko8EhCfx7G8jjG4MVWe4Ckc7gT6W-5DL7u7fSQs23bR-ANzuCyFlUe7RnW3T7yZe88S6mQE9WinkbqapLbqLlCOechgc05fdpMa3PnHYTT3XenQ1Z0aKfMH3UiA1LaMfoenpnB9lN66bXxtUpJw8Z9EAzBrvvhMaWg9Ir_L-xI_CaI