First, sorry for the long text. If this subject is of interest, one may 
want to skip to the code at the bottom.

It seems reflect.StructOf is not doing what the compiler does for first 
field embedded base types with a pointer receiver method set.

reflect.StructOf can't do a lot of things but other shortcomings are 
documented or trigger panics in the call to StructOf. I don't see this 
documented for reflect.StructOf.

StructOf seems to try to mimic the compiler's ability for embedded fields 
when the embedded field is the first field of the list. And when the field 
is a pointer type, it does. But when the field is a base type, the handling 
of the pointer type's method set is handled differently.

The compiler provides access to the field's pointer type's method set when 
the value is addressable, per the spec having to do with base method sets 
and pointer method sets.

The StructOf function comment indicates wrapper methods are not generated 
but my understanding is exposing the method sets from the first field does 
not involve generation. Also, the fact that when the embedded field is a 
pointer type, both method sets for the type are exposed seems to indicate 
the lack of generated method by StructOf is not the issue.  Is it correct 
to think the reflect Value returned from Value.Elem() is also addressable? 
Its fields can be set so I think so.

Do embedded fields need to be pointers?

    Neither the compiler or the reflect package enforce such a constraint. 
 The
    language spec in fact shows the '*' is optional.  This isn't even a real
    question, but the 'Effective Go' document has a section on embedding and
    gives the impression the embedded fields should be pointers. All three
    examples of an embedded field in that section are pointer fields.

    Also the StructOf unit test cases steer clear of testing either 
positively
    or negatively the case of the embedded field not being a pointer but 
having
    a pointer type method set. Many other combinations are tested to ensure
    they work or to ensure they cause a runtime panic.

Should types with a pointer receiver method require embedding with a 
pointer?

   The go compiler does not create this constraint and the reading of when 
pointer type
   method sets are available, when the value is addressable, seems to 
indicate no,
   the embedded field does not need to be a pointer type.

   But the reflect package's StructOf seems to "think" so. The pointer type 
methods are not exposed, only the base type methods are.

There is a part of the language spec, specifically about embedded fields, 
that seems to be saying the reflect package has it right. These two bullet 
points seem to say there is a difference between a field T and a field *T, 
with no mention of whether the field or the struct encompassing it is 
addressable.

>From the language spec:

    Given a struct type S and a defined type T, promoted methods are 
included
    in the method set of the struct as follows:

      o If S contains an embedded field T, the method sets of S and *S both
        include promoted methods with receiver T. The method set of *S also
        includes promoted methods with receiver *T.
      o If S contains an embedded field *T, the method sets of S and *S both
        include promoted methods with receiver T or *T.

So I'm confused. I've used the compiler a long time and the reflect package 
a very short time.  Am I missing something about the reflect package 
mechanics and am I misunderstanding what the last part of the spec about 
embedded fields is saying?

The code below shows what the compiler can do and what the reflect.StructOf 
can't.

The included code contains commented functions that perform similar work 
but with pointer fields. As they show consistency between the compiler and 
the reflect package, they are not necessary to run.

package main

import (
"reflect"
)

type Getter interface{ Get() int }
type Incer interface{ Inc() }

type T struct{ x int }

func (p T) Get() int { return p.x }
func (p *T) Inc()    { p.x++ }

func main() {

CompilerVersion_ValueField() // This does not panic.

ReflectVersion_ValueField() // This panics.

// Two cases that both work. The embedded field is a pointer type.

// CompilerVersion_PointerField() // This does not panic.
// ReflectVersion_PointerField()  // This also does not panic.
}

func CompilerVersion_ValueField() {

var rv2 struct{ T }

(&rv2).T.Inc()   // All
(&(rv2.T)).Inc() // four
(&rv2).Inc()     // compile.
rv2.Inc()        // From spec : If x is addressable and &x's method set 
contains m, x.m() is shorthand for (&x).m().

var i interface{} = &rv2

_ = i.(Getter).Get()
// This does not panic, despite the embedded field T not being a pointer.
i.(Incer).Inc()
}

func ReflectVersion_ValueField() {
// This does not help. Comment at top of #15924 not withstanding.
// var _ interface{} = new(struct {
// T
// })

rt := reflect.StructOf([]reflect.StructField{
{
Name:      "Field0",
Anonymous: true, // define T to be embedded
Type:      reflect.TypeOf(T{}),
},
})
rv2 := reflect.New(rt).Elem()

_ = rv2.Interface().(Getter).Get() // go1.[78] panics here, go1.9+ does not

// go1.13 panics. Inc wasn't wrapped for this StructOf value, presumably 
because the field was not a pointer.
_ = rv2.Interface().(Incer) // <- go1.9+ panic: missing method Inc
}

// func CompilerVersion_PointerField() {
//
// var rv1 struct {
// *T
// }
// var i interface{} = &rv1
//
// // Expect Get and Inc to panic while pointer is still nil.
//
// shouldPanic(func() {
// _ = i.(Getter).Get()
// })
// shouldPanic(func() {
// i.(Incer).Inc()
// })
//
// rv1.T = &T{}
//
// // Expect Get and Inc to no longer panic.
// _ = i.(Getter).Get()
// i.(Incer).Inc()
// }
//
// func ReflectVersion_PointerField() {
//
// rt := reflect.StructOf([]reflect.StructField{
// {
// Name:      "Field0",
// Anonymous: true,
// Type:      reflect.PtrTo(reflect.TypeOf(T{})),
// },
// })
// rv1 := reflect.New(rt).Elem()
//
// // Expect Get and Inc to panic while pointer is still nil.
//
// shouldPanic(func() {
// _ = rv1.Interface().(Getter).Get()
// })
// shouldPanic(func() {
// rv1.Interface().(Incer).Inc()
// })
//
// rv1.Field(0).Set(reflect.ValueOf(func() interface{} {
// v := T{0}
// return &v
// }()))
//
// // Expect Get and Inc to no longer panic.
// _ = rv1.Interface().(Getter).Get()
// rv1.Interface().(Incer).Inc()
// }
//
// func shouldPanic(f func()) {
// defer func() {
// if recover() == nil {
// panic("did not panic")
// }
// }()
// f()
// }




-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/045f4e53-ac1e-4065-aec6-304db14de32f%40googlegroups.com.

Reply via email to