Check out this week’s episode of the Cup o’ Go podcast: What the ʕ◔ϖ◔ʔ? New merch, TDD book interview with Adelina Simion, and more
You can probably guess Go’s rules about recursively embedding interfaces. In short: It’s not allowed.
General interfaces
…
An interface type
T
may not embed a type element that is, contains, or embedsT
, directly or indirectly.// illegal: Bad may not embed itself type Bad interface { Bad } // illegal: Bad1 may not embed itself using Bad2 type Bad1 interface { Bad2 } type Bad2 interface { Bad1 } // illegal: Bad3 may not embed a union containing Bad3 type Bad3 interface { ~int | ~string | Bad3 } // illegal: Bad4 may not embed an array containing Bad4 as element type type Bad4 interface { [10]Bad4 }
Note however, that, an interface method may refer to the interface itself. This is perfectly valid:
type Fooer interface {
Foo() Fooer
}
In fact, it’s actually quite common. You’ll see this pattern with chainable methods. An example can be found in the golang.org/x/exp/slog
package’s Handler
type:
type Handler interface {
/* snip */
// WithAttrs returns a new Handler whose attributes consist of
// both the receiver's attributes and the arguments.
// The Handler owns the slice: it may retain, modify or discard it.
WithAttrs(attrs []Attr) Handler
Here we see that a Handler
implements a WithAttrs
method that in turn returns a Handler
, meaning it is possible to chain these:
var h slog.Handler
h = h.WithAttrs(/* ... */).WithAttrs(/* ... */)
Quotes from The Go Programming Language Specification Version of December 15, 2022