Bypassing unexported field protection using interfaces

351 views
Skip to first unread message

Benno

unread,
Jan 8, 2011, 9:26:49 PM1/8/11
to golang-nuts, be...@benno.id.au
Hi all,

Apologies if this is a duplicate (I posted this yesterday, but it
never seemed to appear on this list?)

I've just started trying to do some real work with Go and came across
the following behaviour that
I can't really understand, and was hoping someone could shed light on
the situation.

The code is:

<<<< ifhack.go
package ifhack

import "fmt"

type Hack struct {
private int
}

func (h Hack) String() string {
return fmt.Sprintf("hack:%d", h.private)
}
>>>>
<<<< ifhackCaller.go
package main

import "fmt"
import "ifhack"

func main() {
var hack ifhack.Hack
var hackptr *ifhack.Hack
var s fmt.Stringer

s = &hack
hackptr = &hack

fmt.Printf("%s\n", hack.String()) // implicit assignment of
unexported field 'private' of ifhack.Hack in method receiver
fmt.Printf("%s\n", (&hack).String()) // implicit assignment of
unexported field 'private' of ifhack.Hack in method receiver
fmt.Printf("%s\n", s.String())
fmt.Printf("%s\n", hackptr.String()) // implicit assignment of
unexported field 'private' of ifhack.Hack in method receiver

fmt.Printf("%s\n", hack) // implicit assignment of unexported field
'private' of ifhack.Hack in function argument
fmt.Printf("%s\n", (&hack))
fmt.Printf("%s\n", s)
fmt.Printf("%s\n", hackptr)
}
>>>>

So, I have a package 'ifhack' that exports a type 'Hack'. That type
has an unexported field 'private'. There is an exported String method
bound to the Hack type.

Now, as I expected I have a hard type actually calling the String()
method as it's receiver is by-value, not by-reference, and calling
by-value requires an implicit copy, and you can't copy a value that
has an unexported field.

What is strange however is that if I indirect via a variable with an
interface type (e.g: variable 's' with type fmt.Stringer), I am then
able to call the String() function.

So, this behaviour raises a number of questino which I'm hoping
someone
can help answer.

1: Is this expected behaviour?

2: If this is expected behaviour, why?

3: Is there anyway to legitimately call an exported method with a by-
value
receiver, when the receiver type has unexported fields?

4: If not, is there any reason why the compiler/type-checker should
allow
this construct in the first place?

Thanks for helping me improve my understanding of Go.

Cheers,

Benno

Ian Lance Taylor

unread,
Jan 8, 2011, 11:50:07 PM1/8/11
to Benno, golang-nuts
Benno <be...@benno.id.au> writes:

> So, I have a package 'ifhack' that exports a type 'Hack'. That type
> has an unexported field 'private'. There is an exported String method
> bound to the Hack type.
>
> Now, as I expected I have a hard type actually calling the String()
> method as it's receiver is by-value, not by-reference, and calling
> by-value requires an implicit copy, and you can't copy a value that
> has an unexported field.
>
> What is strange however is that if I indirect via a variable with an
> interface type (e.g: variable 's' with type fmt.Stringer), I am then
> able to call the String() function.
>
> So, this behaviour raises a number of questino which I'm hoping
> someone
> can help answer.
>
> 1: Is this expected behaviour?
>
> 2: If this is expected behaviour, why?
>
> 3: Is there anyway to legitimately call an exported method with a by-
> value
> receiver, when the receiver type has unexported fields?
>
> 4: If not, is there any reason why the compiler/type-checker should
> allow
> this construct in the first place?

This does seem strange. Please open an issue for this.

At the moment it's not completely clear to me what is correct here. It
seems reasonable that if code can not call a method that it should not
be permitted to assign the object to an interface which uses that
method. That is a little odd, though, because it would mean that an
assignment to an interface type would succeed in the package where the
type is defined but would not succeed in a different package.

Annoyingly, 6g and gccgo behave differently here, and arguably neither
is correct.

Thanks for noticing this.

Ian

Benno

unread,
Jan 9, 2011, 12:34:06 AM1/9/11
to golang-nuts
Submitted as #1402 (http://code.google.com/p/go/issues/detail?
id=1402&colspec=ID%20Status%20Stars%20Priority%20Owner%20Reporter
%20Summary)

Would disabling the creation of these methods in the first place be a
way of solving this problem (as suggested in #3 or #4?)

As far as I can tell, if copying struct with private fields is
considered 'bad', then there is no real reason for exported methods.
(As they can't ever be used externally).

Thanks,

Benno

Gustavo Niemeyer

unread,
Jan 9, 2011, 12:25:27 PM1/9/11
to Benno, golang-nuts
> Would disabling the creation of these methods in the first place be a
> way of solving this problem (as suggested in #3 or #4?)
>
> As far as I can tell, if copying struct with private fields is
> considered 'bad', then there is no real reason for exported methods.
> (As they can't ever be used externally).

I would guess that the proper fix is actually in the opposite
direction. The package a type is defined in is always allowed to copy
values even when they include private fields. The only place one can
define a method on a type is its own package, so that kind of copy
should also be considered internal to the package and thus acceptable.
If the package author doesn't want external use of such a copy then
either a) do not export the type, or b) do not export the method, or
c) use a reference receiver for the method.

--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/blog
http://niemeyer.net/twitter

Ben Leslie

unread,
Jan 14, 2011, 3:42:51 PM1/14/11
to Gustavo Niemeyer, golang-nuts
On Mon, Jan 10, 2011 at 04:25, Gustavo Niemeyer <gus...@niemeyer.net> wrote:
> Would disabling the creation of these methods in the first place be a
> way of solving this problem (as suggested in #3 or #4?)
>
> As far as I can tell, if copying struct with private fields is
> considered 'bad', then there is no real reason for exported methods.
> (As they can't ever be used externally).

I would guess that the proper fix is actually in the opposite
direction.  The package a type is defined in is always allowed to copy
values even when they include private fields.  The only place one can
define a method on a type is its own package, so that kind of copy
should also be considered internal to the package and thus acceptable.
 If the package author doesn't want external use of such a copy then
either a) do not export the type, or b) do not export the method, or
c) use a reference receiver for the method.

Yeah, that works for me. I'm all for letting the developer make the choice.

Although I would still think that value receiver methods would probably have
limited functionality unless you could effectively return a value from them
(which would still not be possible).

Cheers,

Benno 
Reply all
Reply to author
Forward
0 new messages