You mentioned a couple examples. The first example (https://github.com/yohcop/openid-go/blob/master/verify.go#L11-L13):
func Verify(uri string, cache DiscoveryCache, nonceStore NonceStore) (id string, err error) {
return verify(uri, cache, urlGetter, nonceStore)
}
You can see that the unexported verify
function takes an extra urlGetter
argument. This may be something that a client of this package cannot or should not provide. The exported function determines how clients of the package can/should use it; the signature of the non-exported function reflects the dependencies required to do whatever business logic verify
is doing.
The second example(https://github.com/golang/oauth2/blob/master/oauth2.go#L259-L266):
func StaticTokenSource(t *Token) TokenSource {
return staticTokenSource{t}
}
// staticTokenSource is a TokenSource that always returns the same Token.
type staticTokenSource struct {
t *Token
}
This restricts how clients can construct the staticTokenSource
: there is only one way to do it, via the StaticTokenSource
constructor, and it cannot be done directly via a struct literal. This can be useful for many reasons, e.g. input validation. In this case, you want the safety of knowing that the client cannot mutate the t
field on the object, and in order to do this, you leave the t
field unexported. But when it's unexported, the client will not be able to construct the struct literal directly, so you must provide a constructor.
In general, it makes your code much easier to reason about when you can restrict how things are accessed, constructed, or mutated. Golang packages give you a nice mechanism to encapsulate modules of business logic. It's a good idea to think about the conceptual components of your software, and what their interfaces should be. What really needs to be exposed to client code consuming a given component? Only things that really need to be exported should be.
Further reading: Organizing Go code