Skip to content

Commit 1394aca

Browse files
committed
reflect: Add FieldByNameFunc
This adds FieldByNameFunc, which some libraries like reflect2 need. For my usecase I could also just stub FieldByNameFunc to panic, but figured that it would work OK to just make it work. I'm not sure if the overhead to FieldByName using a closure is acceptable. Signed-off-by: Tyler Rockwood <[email protected]>
1 parent 015d2b7 commit 1394aca

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

src/reflect/type.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ type Type interface {
360360
// and FieldByNameFunc returns no match.
361361
// This behavior mirrors Go's handling of name lookup in
362362
// structs containing embedded fields.
363-
//FieldByNameFunc(match func(string) bool) (StructField, bool)
363+
FieldByNameFunc(match func(string) bool) (StructField, bool)
364364

365365
// In returns the type of a function type's i'th input parameter.
366366
// It panics if the type's Kind is not Func.
@@ -710,11 +710,11 @@ func (t *rawType) rawField(n int) rawStructField {
710710
return rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset)
711711
}
712712

713-
// rawFieldByName returns nearly the same value as FieldByName but without converting the
713+
// rawFieldByNameFunc returns nearly the same value as FieldByNameFunc but without converting the
714714
// Type member to an interface.
715715
//
716716
// For internal use only.
717-
func (t *rawType) rawFieldByName(n string) (rawStructField, []int, bool) {
717+
func (t *rawType) rawFieldByNameFunc(match func(string) bool) (rawStructField, []int, bool) {
718718
if t.Kind() != Struct {
719719
panic(&TypeError{"Field"})
720720
}
@@ -757,7 +757,7 @@ func (t *rawType) rawFieldByName(n string) (rawStructField, []int, bool) {
757757

758758
name := readStringZ(data)
759759
data = unsafe.Add(data, len(name))
760-
if name == n {
760+
if match(name) {
761761
found = append(found, result{
762762
rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset),
763763
append(ll.index, int(i)),
@@ -1087,7 +1087,28 @@ func (t *rawType) FieldByName(name string) (StructField, bool) {
10871087
panic(TypeError{"FieldByName"})
10881088
}
10891089

1090-
field, index, ok := t.rawFieldByName(name)
1090+
field, index, ok := t.rawFieldByNameFunc(func(n string) bool { return n == name })
1091+
if !ok {
1092+
return StructField{}, false
1093+
}
1094+
1095+
return StructField{
1096+
Name: field.Name,
1097+
PkgPath: field.PkgPath,
1098+
Type: field.Type, // note: converts rawType to Type
1099+
Tag: field.Tag,
1100+
Anonymous: field.Anonymous,
1101+
Offset: field.Offset,
1102+
Index: index,
1103+
}, true
1104+
}
1105+
1106+
func (t *rawType) FieldByNameFunc(match func(string) bool) (StructField, bool) {
1107+
if t.Kind() != Struct {
1108+
panic(TypeError{"FieldByNameFunc"})
1109+
}
1110+
1111+
field, index, ok := t.rawFieldByNameFunc(match)
10911112
if !ok {
10921113
return StructField{}, false
10931114
}

src/reflect/value.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,17 @@ func (v Value) FieldByName(name string) Value {
18461846
return Value{}
18471847
}
18481848

1849+
func (v Value) FieldByNameFunc(match func(string) bool) Value {
1850+
if v.Kind() != Struct {
1851+
panic(&ValueError{"FieldByName", v.Kind()})
1852+
}
1853+
1854+
if field, ok := v.typecode.FieldByNameFunc(match); ok {
1855+
return v.FieldByIndex(field.Index)
1856+
}
1857+
return Value{}
1858+
}
1859+
18491860
//go:linkname hashmapMake runtime.hashmapMakeUnsafePointer
18501861
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer
18511862

src/reflect/value_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/base64"
55
. "reflect"
66
"sort"
7+
"strings"
78
"testing"
89
)
910

@@ -452,10 +453,20 @@ func TestTinyStruct(t *testing.T) {
452453
t.Errorf("StrucTag for Foo=%v, want %v", got, want)
453454
}
454455

456+
q, ok = reffb.FieldByNameFunc(func(s string) bool { return strings.ToLower(s) == "bar" })
457+
if q.Name != "Bar" || !ok {
458+
t.Errorf("FieldByNameFunc(bar)=%v,%v, want Bar, true", q.Name, ok)
459+
}
460+
455461
q, ok = reffb.FieldByName("Snorble")
456462
if q.Name != "" || ok {
457463
t.Errorf("FieldByName(Snorble)=%v,%v, want ``, false", q.Name, ok)
458464
}
465+
466+
q, ok = reffb.FieldByNameFunc(func(s string) bool { return strings.ToLower(s) == "snorble" })
467+
if q.Name != "" || ok {
468+
t.Errorf("FieldByName(snorble)=%v,%v, want ``, false", q.Name, ok)
469+
}
459470
}
460471

461472
func TestTinyZero(t *testing.T) {

0 commit comments

Comments
 (0)