Skip to content

Commit 83e7a81

Browse files
committed
reflect: add MapIter.SetKey and MapIter.SetValue
These augment the existing MapIter.Key and MapIter.Value methods. The existing methods return new Values. Constructing these new Values often requires allocating. These methods allow the caller to bring their own storage. The naming is somewhat unfortunate, in that the spec uses the word "element" instead of "value", as do the reflect.Type methods. In a vacuum, MapIter.SetElem would be preferable. However, matching the existing methods is more important. Fixes golang#32424 Fixes golang#46131 Change-Id: I19c4d95c432f63dfe52cde96d2125abd021f24fa
1 parent b1aff42 commit 83e7a81

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

src/reflect/all_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"math"
1515
"math/rand"
1616
"os"
17+
"reflect"
1718
. "reflect"
1819
"reflect/internal/example1"
1920
"reflect/internal/example2"
@@ -335,6 +336,28 @@ func TestSetValue(t *testing.T) {
335336
}
336337
}
337338

339+
func TestMapIterSet(t *testing.T) {
340+
m := make(map[string]interface{}, len(valueTests))
341+
for _, tt := range valueTests {
342+
m[tt.s] = tt.i
343+
}
344+
v := ValueOf(m)
345+
346+
k := reflect.New(v.Type().Key()).Elem()
347+
e := reflect.New(v.Type().Elem()).Elem()
348+
349+
iter := v.MapRange()
350+
for iter.Next() {
351+
iter.SetKey(k)
352+
iter.SetValue(e)
353+
want := m[k.String()]
354+
got := e.Interface()
355+
if got != want {
356+
t.Errorf("%q: want (%T) %v, got (%T) %v", k.String(), want, want, got, got)
357+
}
358+
}
359+
}
360+
338361
func TestCanSetField(t *testing.T) {
339362
type embed struct{ x, X int }
340363
type Embed struct{ x, X int }

src/reflect/value.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,6 +1563,29 @@ func (it *MapIter) Key() Value {
15631563
return copyVal(ktype, it.m.flag.ro()|flag(ktype.Kind()), mapiterkey(it.it))
15641564
}
15651565

1566+
// SetKey does dst.Set(it.Key()).
1567+
func (it *MapIter) SetKey(dst Value) {
1568+
if it.it == nil {
1569+
panic("MapIter.SetKey called before Next")
1570+
}
1571+
if mapiterkey(it.it) == nil {
1572+
panic("MapIter.SetKey called on exhausted iterator")
1573+
}
1574+
1575+
dst.mustBeAssignable()
1576+
var target unsafe.Pointer
1577+
if dst.kind() == Interface {
1578+
target = dst.ptr
1579+
}
1580+
1581+
t := (*mapType)(unsafe.Pointer(it.m.typ))
1582+
ktype := t.key
1583+
1584+
key := Value{ktype, mapiterkey(it.it), it.m.flag.ro() | flag(ktype.Kind())}
1585+
key = key.assignTo("reflect.MapIter.SetKey", dst.typ, target)
1586+
typedmemmove(dst.typ, dst.ptr, key.ptr)
1587+
}
1588+
15661589
// Value returns the value of the iterator's current map entry.
15671590
func (it *MapIter) Value() Value {
15681591
if it.it == nil {
@@ -1577,6 +1600,29 @@ func (it *MapIter) Value() Value {
15771600
return copyVal(vtype, it.m.flag.ro()|flag(vtype.Kind()), mapiterelem(it.it))
15781601
}
15791602

1603+
// SetValue does dst.Set(it.Value()).
1604+
func (it *MapIter) SetValue(dst Value) {
1605+
if it.it == nil {
1606+
panic("MapIter.SetValue called before Next")
1607+
}
1608+
if mapiterkey(it.it) == nil {
1609+
panic("MapIter.SetValue called on exhausted iterator")
1610+
}
1611+
1612+
dst.mustBeAssignable()
1613+
var target unsafe.Pointer
1614+
if dst.kind() == Interface {
1615+
target = dst.ptr
1616+
}
1617+
1618+
t := (*mapType)(unsafe.Pointer(it.m.typ))
1619+
vtype := t.elem
1620+
1621+
elem := Value{vtype, mapiterelem(it.it), it.m.flag.ro() | flag(vtype.Kind())}
1622+
elem = elem.assignTo("reflect.MapIter.SetValue", dst.typ, target)
1623+
typedmemmove(dst.typ, dst.ptr, elem.ptr)
1624+
}
1625+
15801626
// Next advances the map iterator and reports whether there is another
15811627
// entry. It returns false when the iterator is exhausted; subsequent
15821628
// calls to Key, Value, or Next will panic.

0 commit comments

Comments
 (0)