Capitalization of fields in private structs

4,417 views
Skip to first unread message

Oliver

unread,
Jan 14, 2015, 5:46:28 AM1/14/15
to golan...@googlegroups.com
A struct type that is not exported can have fields that are capitalized or not:

type myType1 struct {
field1 string
field2 int
}

type myType2 struct {
Field1 string
Field2 int
}

Both types are not visible outside the package so the fields aren't, either. But what's the idiomatic way to name those fields? Or doesn't it matter at all?

I guess if I ever decided to export the type (and its fields), it would be less work to rename just the type and not the fields also. But that's about the only factor I can think of.

Any thoughts/opinions?

-Oliver

Jesse McNelis

unread,
Jan 14, 2015, 6:06:27 AM1/14/15
to Oliver, golang-nuts
On Wed, Jan 14, 2015 at 9:46 PM, Oliver <olive...@gmail.com> wrote:
> Both types are not visible outside the package so the fields aren't, either.
> But what's the idiomatic way to name those fields? Or doesn't it matter at
> all?

The types aren't visible, but you can still pass values of those types
to functions in other packages.
Those functions from other packages will only be able to see the
exported fields on those types.

A common case is the encoding/json package. You might have a type that
isn't exported, but that you want to be able to encode to json.
For the json pkg to see the fields on the struct they'd have to be exported.

Oliver

unread,
Jan 14, 2015, 2:19:48 PM1/14/15
to golan...@googlegroups.com, olive...@gmail.com, jes...@jessta.id.au
Thanks, Jesse, for the explanation. My understanding is you can only pass values of those types to other packages as an interface{} (like in encoding/json). They can't cast back to the original type so the only way to access the fields is by using reflection. You are saying that that's when the capitalization will make a difference. That makes sense.

If I'm not planning on using these values outside my package, however, it doesn't matter, does it?

nicolas riesch

unread,
Jan 14, 2015, 2:56:46 PM1/14/15
to golan...@googlegroups.com
It is possible to access exported fields, even if the struct is not exported, without using reflection :

https://play.golang.org/p/enHSJzInBX

Nigel Tao

unread,
Jan 14, 2015, 7:42:35 PM1/14/15
to Oliver, golang-nuts
On Wed, Jan 14, 2015 at 9:46 PM, Oliver <olive...@gmail.com> wrote:
> Any thoughts/opinions?

My general recommendation is to lower-case names unless you need to.
The idiomatic name for a for loop index is i, not I, even though local
variables aren't part of a package's exported API.

roger peppe

unread,
Jan 15, 2015, 4:04:57 AM1/15/15
to Oliver, golang-nuts
I have seen people use capitalised fields in private structs for
annotation purposes in larger packages - to indicate that those fields
are "public" to code in the rest of the package. There is no actual
enforcement of that convention, of course.
> --
> 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...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Oliver

unread,
Jan 15, 2015, 4:07:09 AM1/15/15
to golan...@googlegroups.com

It is possible to access exported fields, even if the struct is not exported, without using reflection :

https://play.golang.org/p/enHSJzInBX


You're right, I hadn't thought of this case. So even a type that is not exported can be used outside the package. Interestingly, though, godoc will not expose foo. The Newbar() signature will tell you that you're getting something but without looking into the code, you will not know what it is (and that it even has a public field Val).

Not sure that this is so great. I would expect that private types cannot be used in exported functions but they apparently can. I don't see the utility in that but then again, maybe I'm missing something.

nicolas riesch

unread,
Jan 15, 2015, 11:39:40 AM1/15/15
to golan...@googlegroups.com
If the type is not exported, you cannot create an instance by just doing:

   a := &foo.bar{123}

To create an instance, the package "foo" FORCES you to use a function (which acts like a constructor) :

   a := foo.Newbar(123)

It may be useful if the type "bar" is a complex type that needs a lot of initializations.
This way, you are sure that the user cannot create one by himself, and must use your constructor.

nicolas riesch

unread,
Jan 15, 2015, 12:09:22 PM1/15/15
to golan...@googlegroups.com
see also this thread, in particular, comment 7:

https://github.com/golang/go/issues/333


Oliver

unread,
Jan 15, 2015, 3:22:25 PM1/15/15
to golan...@googlegroups.com
Thank you for all of these pointers. So it basically allows me to force users of my package to use the constructor. But one has to keep in mind that they will not be able to work with the value as freely as with exported types. E.g. I can't create a slice:

var s []foo.bar // Compiler error.

or put it in a struct. And, as mentioned, it won't show up in godoc.

Are there any such examples in the standard library? I don't remember having seen any.

Dan Kortschak

unread,
Jan 15, 2015, 3:54:20 PM1/15/15
to Oliver, golan...@googlegroups.com
hash/... and compress/flate use this. The implementation types are not exported, but the New functions return the interface type that describes their behaviour. This interface type is exported and documented.

nicolas riesch

unread,
Jan 15, 2015, 5:39:33 PM1/15/15
to golan...@googlegroups.com
Just as an aside note, it may be useful to know that godoc can display unexported declarations:

  http://golang.org/pkg/hash/crc64/?m=all

See http://godoc.org/golang.org/x/tools/cmd/godoc:
    "all      show documentation for all declarations, not just the exported ones"

I remember that it was a little difficult for me to understand the "hash" package, because of that unexported type issue.

Oliver

unread,
Jan 16, 2015, 2:18:44 AM1/16/15
to golan...@googlegroups.com
I'm a bit confused here. I can't find an example of what we talked about in any of these packages. The constructors all return exported types or interfaces, all capitalized. The extended godoc for crc64 shows a private type digest but it's not returned anywhere.

What am I missing?

Dan Kortschak

unread,
Jan 16, 2015, 5:33:24 AM1/16/15
to Oliver, golan...@googlegroups.com
Look at the type this function returns according to its signature and the type that it prepares for the return.

http://golang.org/src/crypto/md5/md5.go?s=913:933#L41

Oliver

unread,
Jan 16, 2015, 5:58:55 AM1/16/15
to golan...@googlegroups.com, olive...@gmail.com
Hmm, maybe I'm not clear about what I'm referring to. This example is perfectly fine. The return type in the signature is an exported interface. I wouldn't be concerned with what's actually returned, I have a guarantee that it satisfies the interface for which there is a public declaration.

What we were discussing further above, and what I'm looking for in the standard library is something like this:

func New() *digest {
...
}


Returning a private type in a public function compiles and works. But we don't really know what we're getting (unless we look into the code or make godoc show all private types, too) and our usage of the value is quite limited (see above). I would dare to say this is bad style - and the proper way is to return an exported interface instead as in crypto/md5 - but maybe there is an example in the standard library that illustrates a case (returning a private type in a public function) where this makes sense.

Matt Harden

unread,
Jan 16, 2015, 10:45:39 PM1/16/15
to Oliver, golan...@googlegroups.com
Even if returning an interface type with an unexported struct value, I still wouldn't include exported fields in that struct, because it is still possible to access those fields either via reflection, or by recreating the struct exactly, which then allows you to get at the fields without reflection. I think the second case only works if all of the fields are exported.

--
Reply all
Reply to author
Forward
0 new messages