Skip to content

Instantly share code, notes, and snippets.

@mumbleskates
Last active November 30, 2023 02:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mumbleskates/f6328a10dcefa4445b6cee7683f8cb1e to your computer and use it in GitHub Desktop.
Save mumbleskates/f6328a10dcefa4445b6cee7683f8cb1e to your computer and use it in GitHub Desktop.
// It is possible to add golang interfaces together.
// But did you know that you can also subtract them?
// Here are two arbitrary interfaces. They can have any
// methods.
type A interface {
Foo() string // This is the conflicting method signature.
Bar() int
}
type B interface {
Foo() string // Both A and B have it.
Baz() bool
}
// This is a normal combined interface: it has the union of
// all methods in both A and B.
type AunionB interface {
A
B
}
// The above doesn't work if there are colliding methods with
// the same name but different signatures, you have to spell
// the intersection manually with all the appropriate method
// names (though the arguments and return types don't matter,
// and the resulting struct at the end won't match A's
// interface anyway which might defeat the point. But this
// can be done with zero bytes of overhead, so that's nice!)
// This struct's interface is only the methods that are *not*
// common between A and B; any methods that are common are
// ambiguous when called, so they are not callable and
// therefore missing.
type AdiffB struct {
A // both of these fields are interface *objects*
B
}
// This struct's interface is the *intersection* of the
// methods in A and B: they can't be dispatched to AdiffB
// because they conflicting there, so they are always sent to
// AunionB. Methods not in the intersection of A and B are
// present in both types this struct inherits from and so
// are missing.
type AintersectB struct {
AunionB
AdiffB
}
// This type's interface is only methods in A but not B...
type Aonly struct {
A
AintersectB
}
// And finally, we have a struct which has an interface
// matching AunionB, with a B interface object field that is
// dispatched to for all its methods, if possible, and any
// methods not part of the B interface are dispatched to the
// nested A interface object.
type AoverlaidwithB struct {
Aonly
B
}
// This type matches either or both interfaces.
var _ A = AoverlaidwithB{}
var _ B = AoverlaidwithB{}
var _ AunionB = AoverlaidwithB{}
// Here's a couple concrete types we can put into those
// interfaces to demonstrate.
type Areal struct{}
func (a Areal) Foo() string {
return "real A impl"
}
func (a Areal) Bar() int {
return 5
}
type Breal struct{}
func (b Breal) Foo() string {
return "real B impl"
}
func (b Breal) Baz() bool {
return true
}
func main() {
x := AoverlaidwithB{
Aonly: Aonly{A: Areal{}},
B: Breal{},
}
// All the other nested fields can be left nil, as they will
// never be automatically dispatched to. The total overhead
// for all these shenanigans is 3 empty interface objects,
// or 48 bytes of zeros on a 64b platform.
fmt.Printf(
"foo: %q, bar: %d, baz: %t\n",
x.Foo(), x.Bar(), x.Baz(),
)
// prints:
// foo: "real B impl", bar: 5, baz: true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment