Golang Generics

Xing Du
2 min readJun 14, 2022

--

Generics are finally available in Golang 1.18!

Generics improve the reusability of source code and reduce the redundancy/verbosity of source code.

With rich experience with Java generics, I went ahead with a quick test for Golang generics today. The TL;DR; version is:

It’s similar with a small drawback. Definitely better than not having generics, but have a limitation on methods.

Set up an interface to be used as a type constraint:

type Fooable interface {
Foo() int64
}

And 2 implementations to test with:

type fooImpl struct {}func (fooImpl) Foo() int64 {
return 0
}
type anotherFooImpl struct {}func (anotherFooImpl) Foo() int64 {
return 1
}

Test generic functions:

func Consume[F Fooable](fooable F) {
fmt.Printf("foo=\"%d\"\n", fooable.Foo())
}
// to test:
// Consume(fooImpl{})
// Consume(anotherFooImpl{})

Test generic struct:

type Consumer[F Fooable] interface {
Consume(foo F)
}
type consumerImpl[F Fooable] struct {}func (c *consumerImpl[F]) Consume(fooable F) {
fmt.Printf("foo=\"%d\"\n", fooable.Foo())
}
// to test:
// d := &consumerImpl[fooImpl]{}
// d.Consume(fooImpl{})
// d.Consume(anotherFooImpl{})

Test generic method:

type genericConsumer struct {}
func (g *genericConsumer) Consume[F Fooable](fooable F) {
fmt.Printf("foo=\"%d\"", fooable.Foo())
}
// to test
// g := &genericConsumer{}
// g.Consume(fooImpl{})
// g.Consume(anotherFooImpl{})

Results:

Generic functions and structs work as expected, but the compiler doesn't like generic methods. Did a bit of googling and found this thread, not sure if this will ever be supported at the language level.

Full source code if anyone’s interested: (alternatively, playground)

package mainimport (
"fmt"
)
type Fooable interface {
Foo() int64
}
// non-object-orientedfunc Consume[F Fooable](fooable F) {
fmt.Printf("method=\"Consume\" foo=\"%d\"\n", fooable.Foo())
}
// set up for generic struct
type Consumer[F Fooable] interface {
Consume(foo F)
}
type fooImpl struct {}func (fooImpl) Foo() int64 {
fmt.Printf("struct=\"fooImpl\" method=\"Foo\"\n")
return 0
}
type anotherFooImpl struct {}func (anotherFooImpl) Foo() int64 {
fmt.Printf("struct=\"anotherFooImpl\" method=\"Foo\"\n")
return 1
}
type consumerImpl[F Fooable] struct {
}
func (c *consumerImpl[F]) Consume(fooable F) {
fmt.Printf("struct=\"consumerImpl\" method=\"Consume\" foo=\"%d\"\n", fooable.Foo())
}
// type genericConsumer struct {}
// func (g *genericConsumer) Consume[F Fooable](fooable F) {
// fmt.Printf("struct=\"genericConsumer\" method=\"Consume\" foo=\"%d\"", fooable.Foo())
// }
func main() {

// non-object-oriented.
fmt.Printf("section=\"non-object-oriented\" msg=\"begin\"\n")
Consume(fooImpl{})
Consume(anotherFooImpl{})
fmt.Printf("section=\"non-object-oriented\" msg=\"done\"\n\n")
fmt.Printf("section=\"typed struct\" msg=\"begin\"\n")
d := &consumerImpl[fooImpl]{}
d.Consume(fooImpl{})
// this would fail since `anotherFooImpl` doesn't neet the generic constraint for `consumerImpl`
// d.Consume(anotherFooImpl{})
fmt.Printf("section=\"typed struct\" msg=\"done\"\n\n")
// as of golang 1.18.3, this is not allowed:
// > syntax error: method must have no type parameters
// more details: https://github.com/golang/go/issues/49085
// g := &genericConsumer{}
// g.Consume(fooImpl{})
// g.Consume(anotherFooImpl{})
}

--

--

Xing Du
Xing Du

Written by Xing Du

Minimalist. Game Developer. Software Engineer. DevOps enthusiast. Foodie. Gamer.

No responses yet