I’ve done some blogging on & off in the past and always felt it lacks something. This is a new try in blogging (certainly with some positive reasons), preferably short entries describing one thing. Let’s go.

Preparation Story

The other day I wanted to implement a single instance app, on Ubuntu. So if an instance is already running, no other instance would start. After some googling and actually some help, I’ve learnt that Unix Domain Socket is a reliable tool to do this.

After some struggling with it - the name of the socket was the problem and the hard part, as we all know - the solution shaped into this function:

// CheckInstance for implementing single instance app, filePath should be like
// "@/tmp/lablock", @ will be added anyway, if not provided, 
// current app name will be used
func CheckInstance(filePath ...string) (Closer, error) {
	var fp string
	if len(filePath) > 0 {
		fp = filePath[0]
	} else {
		fp = filepath.Base(os.Args[0])
		fp = filepath.Join("/tmp/", fp)
	}

	fp = strings.Trim(fp, "@")
	fp = "@" + fp

	l, err := net.ListenUnix("unix", &net.UnixAddr{Name: fp, Net: "unix"})
	if err != nil {
		return nil, err
	}

	return l, nil
}

A sample usage in the main function of an app, would look like:

func main() {
    ins, err := mypackage.CheckInstance()
    if err != nil {
        log.Println(`error:`, err)
        return
    }
    defer ins.Close()

    // ...
}

The code is straightforward. If we fail to open a socket, then the app is already running, otherwise we continue with our app logic and close the socket on exit.

Problem?

As you see our code depends on net.ListenUnix(...) which returns an instance of *net.UnixListener. We want the user to close this socket when she/he is done and nothing else.

What’s the problem? The problem is there is no need for the user to have an instance of *net.UnixListener in hers/his code and for certain we do not want her/him to do anything with the socket but close it.

Should we write a wrapper? We could but there is no need.

It’s already solved (almost)

This is where Go interfaces play their clean magic. We just define a new interface:

// Closer anything that can be closed
type Closer interface {
	Close() error
}

And as you see mypackage.CheckInstance() just returns a Closer. What exactly just happened? What happened to the *net.UnixListener?

That’s the clean magic of Go interfaces! *net.UnixListener has already implemented the Closer interface. In fact we have defined an interface that is already implemented!

So we achieved our goal of hiding the dependency of our library on *net.UnixListener from the user, by defining an interface that is already implemented - no need for a new wrapper type & creating it’s instances and handling them thanks to Go and it’s interfaces.