copying structs with unexported fields

493 views
Skip to first unread message

Russ Cox

unread,
Mar 14, 2011, 2:13:01 PM3/14/11
to golang-dev
I am wondering whether we should allow structs
containing unexported fields to be assigned by other
packages. Currently we do not, but it complicates
things.

The current rule

* makes it impossible to return a plain struct value
with unexported fields in an API, even if that is
otherwise appropriate, because the caller cannot
even store it in a local variable (x := p.F()).

* complicates the rules for when you can and cannot
use the copy and append builtins (6g does not get
this right).

* complicates the rules for when you can pass such
a value back to a function in the original package
(we just added a special case hack to the spec
for method receivers).

* is not a consequence of the lexical interpretation
of the export rules, as pointed out recently by atom symbol.

I think that putting partial restrictions on what can and
cannot be copied is not in line with the spirit of other
simplifying assumptions in Go like the zero value.
Removing the copying restriction would result in a very
simple rule: you can always copy a value. It would also
let us delete the receiver copying hack from the spec.

At the time we added the assignment restriction, the
main motivation as I remember it was to give packages
a way to make sure that they could expose values and
be 100% sure that other packages could not, say, copy
an opaque unique id field from one structure to another.
More mechanism is unnecessary here: if a package
wants to return uncopyable data, it can return an
interface value containing a pointer to that data.
That is, interface values already address the issue.
We probably don't need a second mechanism.

Something to think about. Comments welcome.

Russ

Rob 'Commander' Pike

unread,
Mar 14, 2011, 2:23:27 PM3/14/11
to r...@golang.org, golang-dev
It might be a good idea to relax this restriction, but I will observe that every time I can remember seeing the compiler diagnostic, it has been because I have made a programming error.

-rob

Gustavo Niemeyer

unread,
Mar 14, 2011, 2:55:38 PM3/14/11
to r...@golang.org, golang-dev
> simple rule: you can always copy a value.  It would also
> let us delete the receiver copying hack from the spec.

+1 on allowing it. I've already wished before that this was the case.

That said, it's important to at least mention in this conversation the
impact on packages which take into account the address of a variable,
such as anything that depends on runtime semaphores and now the
sync/atomic package (e.g. Mutex, Cond, etc). These are cases which
will be affected, and I don't think we'll want to access them via an
interface at all times.

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

Russ Cox

unread,
Mar 15, 2011, 1:17:05 PM3/15/11
to Gustavo Niemeyer, golang-dev
> That said, it's important to at least mention in this conversation the
> impact on packages which take into account the address of a variable,
> such as anything that depends on runtime semaphores and now the
> sync/atomic package (e.g. Mutex, Cond, etc).  These are cases which
> will be affected, and I don't think we'll want to access them via an
> interface at all times.

I think those would not change.
Don't copy a structure that someone else
might be editing. The fact that it might be
locked is but one of many problems.

Russ

roger peppe

unread,
Mar 16, 2011, 9:01:55 AM3/16/11
to r...@golang.org, Gustavo Niemeyer, golang-dev
it has to be said that it is nice that
the compiler currently catches the following error:

type someType struct {
mu sync.Lock
data someData
}

func (x someType) String() string {
x.mu.Lock()
defer x.mu.Unlock()
return data.String()
}

also, if the only way to ensure non-copying is
to use an interface, there's a penalty to pay,
particularly when the compiler can do inlining.

however i think i'm in favour of this proposal, on balance.
i like the simplification and i have wanted to have
by-value structs with private fields in the past.

however, here's another possibility:

we could disallow copying
of unexported types in external packages.
that doesn't simplify things as much (it addresses
points 1 and 3 on your list), but does
make it possible to have the best of both worlds.

the fact that you can currently use unexported types
even though you can't refer to them is a bit odd;
this would clear that up and add something useful
into the bargain.

package a

type someType byte
func (someType) Foo() {}

// struct not copyable by other packages, as it
// holds an unexported type directly.
type Container1 struct {
x someType
}

// also not copyable for the same reason;
// currently copying would be allowed.
type Container2 struct {
Foo someType
}

// copyable, as type not used directly
type Container3 struct {
foo *someType
}

// currently this function is legal, but
// nearly useless. compiler could give
// an error "exported function returns unexported type"
func SomeFunc() someType {
return 0
}

// currently this is legal, and this idiom is used in at least one
// place (encoding/binary), but could give
// a compiler error and be easily rewritten...
var SomeVar someType

// ... as follows, without loss of generality
type OtherType struct {
unused byte
}

// legal because OtherType contains no unexported
// fields, even though it does contain unexported members.
func (OtherType) Foo() {}

var OtherVar OtherType

Reply all
Reply to author
Forward
0 new messages