method visibility in reflection API

237 views
Skip to first unread message

chressie

unread,
Mar 29, 2010, 8:48:34 AM3/29/10
to golang-nuts
Hi,

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

roger peppe

unread,
Mar 29, 2010, 9:12:15 AM3/29/10
to chressie, golang-nuts
from here: http://golang.org/doc/go_spec.html#Types

"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).

Christian Himpel

unread,
Mar 30, 2010, 5:25:07 AM3/30/10
to golang-nuts, roger peppe, sv3...@gmail.com
On Mon, Mar 29, 2010 at 3:12 PM, roger peppe <rogp...@gmail.com> wrote:
> 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.

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

Esko Luontola

unread,
Mar 30, 2010, 6:27:02 AM3/30/10
to golang-nuts
The thing with methods of receiver type *T or T is, that a method of
type T cannot modify the object instance (because a copy of the
instance is passed to the method instead of the original method),
whereas a method of type *T can modify it. So it's safe to pass a *T
to a T method, but not the other way around.

Christian Himpel

unread,
Mar 30, 2010, 7:13:19 AM3/30/10
to golang-nuts, Esko Luontola, sv3...@gmail.com
On Tue, Mar 30, 2010 at 12:27 PM, Esko Luontola <esko.l...@gmail.com> wrote:
> The thing with methods of receiver type *T or T is, that a method
> of type T cannot modify the object instance (because a copy of the
> instance is passed to the method instead of the original method),
> whereas a method of type *T can modify it.

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

roger peppe

unread,
Mar 30, 2010, 8:03:25 AM3/30/10
to Christian Himpel, golang-nuts, Esko Luontola, sv3...@gmail.com
On 30 March 2010 11:13, Christian Himpel <chre...@googlemail.com> wrote:
>> 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

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.

Reply all
Reply to author
Forward
0 new messages