we encountered a phenomenon using the reflection API we don't
understand.
The codesample attached to this mail tries to illuminate the problem:
A method declared with a pointer type receiver (line 7) seems not to
be
visible via the reflection API when examining its value type (line
10).
OTH the method is visible when examining its pointer type (line 11).
1 package main
2
3 import "reflect"
4
5 type User struct { Id int }
6
7 func (u *User) AddOne() int { return u.Id + 1 }
8
9 func main() {
10 println(reflect.NewValue(User{1}).Type().NumMethod()) //
prints 0
11 println(reflect.NewValue(&User{1}).Type().NumMethod()) //
prints 1
12 }
BTW, a method declared with a value type receiver is visible in both
cases.
Is this the expected behaviour? If so is it somehow possible to get a
list of all declared methods from a value type?
Regards,
sven & chressie
"The method set of any other named type T consists of all methods
with receiver type T. The method set of the corresponding pointer
type *T is the set of all methods with receiver *T or T (that is, it also
contains the method set of T)."
so your code is working correctly. the first print will print
the count of methods that work on User; the second will
print the count of methods on User and *User.
unfortunately i don't think there's any way to create new
types via reflect, so given a reflect.Type representing T, i don't think it's
possible to create a Type representing *T.
i could imagine an extension to reflect.Type that would
allow this e.g.
type Type interface {
....
PointerTo() Type
}
but i don't know how difficult this would be (after all, the compiler
might have seen that *T is never used, and deleted all information
about the pointer methods from the executable).
We noticed this too when scanning the API.
> i could imagine an extension to reflect.Type that would allow this
> e.g.
>
> type Type interface {
> ....
> PointerTo() Type
> }
Indeed, this would solve our problem.
> but i don't know how difficult this would be (after all, the
> compiler might have seen that *T is never used, and deleted all
> information about the pointer methods from the executable).
We neither :)
Maybe we might highlight the usefulness of this addition with our
use case:
We try to extend the markup language mustache[1] with the ability to
call methods. The current solution isn't able to support calling
methods declared on pointer type values for obvious reasons. Your
suggested API enhancement would do the trick.
[1]: <http://github.com/sschober/mustache.go/blame/master/mustache_test.go>
Regards
Agreed.
> So it's safe to pass a *T to a T method, but not the other way
> around.
That's a bit confusing, as you can call a pointer type receiver
method completely transparently on a value type, like:
type T struct { I int }
func (t *T) Inc() { t.I++ } // pointer type receiver
t := T{0} // create value type t
t.Inc() // manipulates the value type t
So a PointerTo method should not break any (silent) contracts.
Or am I missing something obvious?
Regards
this is a special case - and it doesn't work if t is an interface type
or is not addressable.
both the following are illegal:
t{0}.Inc()
(func()T{return T{0}}).Inc()
> So a PointerTo method should not break any (silent) contracts.
regardless of the above, i don't think a PointerTo method on Type
would break any contracts because we'd be manipulating the type,
not the value. we'd be able to create a new instance of the pointer version
of the type, but we wouldn't be able to change the old value.
the difficulty with this change to reflect (and it applies to other
"type creation" methods that it would be nice to see too, such as SliceOf
and ChanOf) is that it requires the addition of a mutable runtime table
containing all types. type equality in reflect is defined as pointer equality,
so to create a new type, you'd need to enter it in the table if not
already present
so that any other code creating that type uses the same pointer.
currently that table must exist in the compiler, but it doesn't at
run-time AFAICS.
the challenge of doing so efficiently (in space and time) is probably
why these methods don't currently exist in the reflect package.