Is it considered bad style to return an unexported type from an exported function?
When I've used it, I mostly find it just creates problems.
A better question might be: when is it a good idea to return an unexported type from an exported function.
I would say there's nothing wrong with returning an unexported variable from an exported function. That's what an accessor is by definition.
That said, I would only do that in the case where there was some logic that needed to occur every time the unexported variable needed to be accessed.
EDIT: I hope I understand your clarified question.
If you have an unexported type user struct{}
and you return it with NewUser()
, would that fit your use case? If so, then that is a factory design pattern and is useful in Go if you do not want a 3rd party developer to directly create a user
type object. That way, your "constructor" or "factory" is the only place to get new instances.
So, is it "bad style"? I'd say it depends on what challenge needs to be overcome.
Golang's linters return a warning when you return an unexported type, so I'd say though it's technically possible it's also to be discouraged. 1
One reason is that some code that receives the unexported type cannot utilise it for various "signatures" (eg: types, funcs), without typing it as an interface (eg: interface{} or otherwise)
eg:
package people
type me string
func NewMe() me {
return "me"
}
// in main.go
package main
type eff struct {
m *people.me // <-- cant do this
}
func main() {
var m people.me // <-- cant do this
m2 := people.NewMe() // can do this
}
I think it's worth posting another viewpoint for others that happen upon this question. A better way to address this is to either define an actual exported interface for that type if the user is meant to interact with it in any way, or just don't return unexported types so you won't have to address this situation. This is primarily a design problem, let me explain.
While it's technically possible to return an unexported type, as shown above, the type must be assigned to interface{}
for code outside of that package. That to me is a code smell and is something I would seriously question in a code review. Similarly, I would question an exported function/method accepting unexported parameters.
The biggest problem I have with this is that the function signature does not clearly convey its scope of use. Is it an internal function since it returns an internal type? Is it meant to be called by outside consumers since it's an exported function? It's really ambiguous.
Plus, while returning an interface{}
makes this type transferable, it also breaks type safety to a certain degree. If the purpose of returning interface{}
is to prevent the user from changing data before passing it to another part of the API, then the secondary API needs to then accept interface{}
and check for a valid type before using it. Also code smelly and the compiler can't help us with bad calls here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With