# Polymorphic, Recursive Interfaces Using Go Generics

DevFeed: [Polymorphic, Recursive Interfaces Using Go Generics](<https://devfeed.tech/articles/polymorphic-recursive-interfaces-using-go-generics-29342.md>)

Original publisher: [Read original article](<https://multithreaded.stitchfix.com/blog/2023/02/01/go-polymorphic-interfaces/>)

Published: 2023-02-01T09:00:00Z

Content type: tutorial

Language: en

Sources: [Stitch Fix](<https://devfeed.tech/sources/stitch-fix.md>)

Topics: [Go Language](<https://devfeed.tech/topics/go-language.md>), [generics](<https://devfeed.tech/topics/generics.md>), [interfaces](<https://devfeed.tech/topics/interfaces.md>)

Tags: [code](<https://devfeed.tech/tags/code.md>), [generics](<https://devfeed.tech/tags/generics.md>), [go](<https://devfeed.tech/tags/go.md>), [interfaces](<https://devfeed.tech/tags/interfaces.md>)

## AI overview

The article explains how Go generics enable polymorphic interfaces whose methods return the concrete receiver type, using logger implementations as an example. It also discusses the limitations of Go's type inference, which can require explicit type parameters when using these interfaces.

## Source excerpt

I did a bit of experimenting over the weekend in an attempt to figure out how to write an interface that abstracted over methods whose return type was identical to the receiver's type. This is frequently a thing that one wants to do when abstracting over self-cloning objects, or things that implement the Builder pattern. For example, suppose we have a struct through which we log stuff: type StdoutLogger struct { out ioutil.Writer outMu sync.Mutex fields map[string]interface{} } func (n *StdoutLogger) WithFields(fields map[string]interface{}) (out *StdoutLogger) { for k, v := range fields { out.fields[k] = v } return } func (n *StdoutLogger) Infof(format string, args ...interface{}) { n.outMu.Lock() defer n.outMu.Unlock() s := fmt.Sprintf(format, args...) if len(n.fields) > 0 { s += " " } for k, v := range n.fields { s += fmt.Sprintf("%s=%+v", k, v) } n.out.Write([]byte(s)) } ...and we've got some other struct that we use during test which ignores all requests to log stuff: type NoopLogger struct {} func (n *NoopLogger) WithFields(fields map[string]interface{}) *NoopLogger { return n } func (n NoopLogger) Infof(format string, args ...interface{}) { return } Before Go Generics Before the Go "generics" feature was released, defining an interface that abstracted over both structs was not possible (link). For example, if we had a pre-generics interface that looked like: type Logger interface { WithFields(fields map[string]interface{}) Logger Infof(format string, args ...interface{}) } ...there would be no way to satisfy it with types that had these signatures: func (n *NoopLogger) WithFields(fields map[string]interface{}) *NoopLogger func (n *StdoutLogger) WithFields(fields map[string]interface{}) *StdoutLogger ...because of the different return types of each struct's WithFields method. Polymorphic Interfaces Now that generics have landed, we can define an interface that abstracts over both of these structs: type Logger[T any] interface { WithFields(fields map[string]interface