Embedding Types to Reuse Code with Less Noise

2020-04-30 • edited 2022-01-05 • 2 minutes to read

My previous post was about one of the concurrency patterns that can be achieved with go. I immediately got some feedback on it, and this is where I will address one of them.

You have seen the code reimplementing sync.WaitGroup to have a semaphore in it.

type WaitGroup interface {
	Add(delta int)
	Done()
	Wait()
}

type SemaphoredWaitGroup struct {
	sem chan bool
	wg  sync.WaitGroup
}

func (s *SemaphoredWaitGroup) Add(delta int) {
	s.wg.Add(delta)
	s.sem <- true
}

func (s *SemaphoredWaitGroup) Done() {
	<-s.sem
	s.wg.Done()
}

func (s *SemaphoredWaitGroup) Wait() {
	s.wg.Wait()
}

It has been brought to my attention (Thanks, Wojtek !), that there is a “cleaner” way to do it in Go.

Enter: Type Embedding

Those coming from other languages (e.g.: PHP), you might notice that there is no such thing as an extends keyword. There is no subclassing in the popular way. What we do have is type embedding .

To achieve that we change SemaphoredWaitGroup:

type SemaphoredWaitGroup struct {
	sem chan bool
-	wg  sync.WaitGroup
+	sync.WaitGroup
}

All methods from the “inner” (embedded) struct are now a part of the “outer” (embedding) type. It is even possible to access them directly by calling them as if they were actually defined locally. E.g:

func (s *SemaphoredWaitGroup) newMethod() {
	s.Wait()
}

There is a catch though. when we re-define the methods from the inner part (here we have: Add(delta int) and Done()), we need to change the internal calls

func (s *SemaphoredWaitGroup) Add(delta int) {
-	s.Add(delta)
+	s.WaitGroup.Add(delta)
	s.sem <- true
}

func (s *SemaphoredWaitGroup) Done() {
	<-s.sem
-	s.Done()
+	s.WaitGroup.Done()
}

because the s.Add(delta) and s.Done() would be recursive calls, and would result in a “stack overflow ” error in this particular instance.

Also, we don’t need to have the Wait() method pass calls through to the underlying sync.WaitGroup struct.

A complete example is available on Go Playground .

software engineeringgocompositioninheritancepolymorphism

Homemade CLI Tools

Semaphored Wait Group

comments powered by Disqus