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.