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{})
}