I am currently looking through some patterns and how they can be implemented in Go, using the following site : Design Patterns in Go
If I want to create the Singleton pattern, but inside a package, accessed from another package, then I will get a warning from the linter : Exported function with the unexported return type. This is because the struct single is not exported.
You can go for an unexported type, but that makes things quite awkward for callers because they get a value that's difficult to pass around, since the type cannot be referred to.
In the case of a Singleton you should never actually need to pass around the object though, you can just call GetInstance() again in the function that needs the object.
Now to my question : Is the Singleton pattern (inside a package) a case where it is actually correct/accepted to use an exported function with the unexported return type? Or is there a better Go-way of creating a Singleton (inside a package)?
Example using the init function (for sync.Mutex and sync.Once variations, see the first link):
package single
import "fmt"
type single struct {
}
var singleInstance *single
func GetInstance() *single { // <---- Warning here
fmt.Println("Single instance already created.")
return singleInstance
}
func init() {
fmt.Println("Creating single instance now.")
singleInstance = &single{}
}
Hello there. I use this web site with patterns too. But point your attention that the code provided there is the Conceptual Example. This is how you can write your own singleton struct in go, but it does not mean it will be exactly the way it’s shown there. Export of the variables from the packages is the decision of the developer. If any of the types, variables, functions, interfaces etc, you declared, need to be used outside the package, then they should be exported. Unexported stuff is mostly used to hide unnecessary details of the process, wrap functions e.g. different architectures or store internal values, so no one can change them besides your code. In your case, if the singleton of this struct is needed outside the package, just make it exported.
I understand unexported vs exported. My question is more about the warning and people’s comments in the reddit-thread.
If I create a Singleton package (for some unspecified reason, for example a database connection), then making the Single struct exported would mean that anyone could instantiate that type whenever they wanted, rendering the Singleton pattern useless in this case.
So, my question basically is, is the Singleton pattern a case where exporting an unexported struct actually is necessary? People in the Reddit-thread seemed very sure that it is a bad idea, but I don’t see how I can implement a Singleton without leaving the single struct unexported.
In your case, if the singleton of this struct is needed outside the package, just make it exported.
A Singleton should never be instantiated randomly by other people, that’s the whole purpose of the pattern. It should be created once, and then be reused every time someone asks for it. So making it exported makes no sense to me.
Hopefully this explains my question a little bit better…thanx for trying to make sense of my question btw
Ps. I guess part of the problem is that I hate having warnings in my code. I feel like I am doing something wrong when there are warnings. But in this case, I don’t see how I can create a true Singleton without this warning.
Personally I would agree with reddit. I would say it’s something to avoid the possible confusion in future. Another way of solving it, is at least to return exported interface, so you can understand what it is used for and why.
Ps. What linter shows you a problem with this type? My vs code is silent and golangci-lint as well