In Effective Go, it's clearly stated that in the case where a field is unexported (begins with a lower case letter) a getter method should have the same field name but starting with upper case; the example they give is a owner
as a field and Owner
as a method. And they explicitly advise against using Get
in front of the getter method name.
I frequently encounter situations where I need the field to be exported for JSON marshaling, use with an ORM, or other reflection related purposes (IIRC reflect can read but not modify unexported fields), so my field would need to be called Owner
in the example above and thus cannot have an Owner
method.
Is there an idiomatic way of naming which addresses this situation?
EDIT: Here's a concrete example of what I'm running into:
type User struct {
Username string `db:"username" json:"username"`
// ...
}
// code in this package needs to do json.Unmarshal(b, &user), etc.
.
// BUT, I want code in other packages to isolate themselves from
// the specifics of the User struct - they don't know how it's
// implemented, only that it has a Username field. i.e.
package somethingelse
type User interface {
Username() string
}
// the rest of the code in this other package works against the
// interface and is not aware of the struct directly - by design,
// because it's important that it can be changed out without
// affecting this code
Don't use getters and setters if your fields are exported. This just confuses the interface.
If you need a getter or setter, because it does something (validation, formatting, etc), or because you need the struct to satisfy an interface, then don't export the underlying field!
If you need an exported field for the sake of JSON, database access, etc, and you need a getter/setter, then use two structs, one exported and one private, and define a custom JSON marshaler (or db access method) on the public one:
type jsonFoo struct {
Owner string `json:"owner"`
}
type Foo struct {
owner string
}
func (f *Foo) SetOwner(username string) {
// validate/format username
f.owner = username
}
func (f *Foo) Owner() string {
return f.owner
}
func (f *Foo) MarshalJSON() ([]byte, error) {
return json.Marshal(jsonFoo{
Owner: f.owner,
})
}
I'm going to post this as an answer, derived after considering @Flimzy's answer, which makes a lot of sense.
The basic idea is to have one struct with exported fields which can be used for marshaling, and another separate struct which has the sole purpose of providing the methods which meet the needed interface.
This requires no custom marshaling code, and gives a clear meaning to each struct, IMO:
type user struct {
Username string `json:"username"`
}
type ExportedUser struct {
// EDIT: explicitly doing "user *user" instead of just doing "*user"
// helps avoid confusion between between field names and methods
user *user
}
func (u ExportedUser) Username() string { return u.user.Username }
func main() {
fmt.Printf("Test: %q", ExportedUser{user: &user{Username: "joe"}}.Username())
}
The question of whether it should be user
or User
above is a bit unclear to me - since it may make sense for the type to be visible for use in a Go template, e.g. {{euser.User.SomeOtherField}}
Thus allowing all of the fields to be accessible if needed. Regardless, the answer above functions the same either way.