There's been a lot of criticism about Go recently because it doesn't have support for generics. What exactly does that mean? How would you explain that to someone coming from a dynamically typed language like Ruby where this isn't a familiar concept?
-
2Java generics: http://stackoverflow.com/questions/19848961/generics-definition c# generics: http://stackoverflow.com/questions/4560890/what-is-generics-in-c should give you the idea – geedubb Jan 17 '14 at 21:28
-
1The quick answer to the question in the title is "Nonexistent." – Keith Thompson Jan 17 '14 at 22:52
-
Note that a generics 'library' has been recently created for go ... [gen](http://clipperhouse.github.io/gen/) ... it is really more of a code generator. From the docs - _gen generates code for your types, at development time, using the command line. gen is not an import; the generated source becomes part of your project and takes no external dependencies._ – dodgy_coder Jan 21 '14 at 05:02
3 Answers
In a dynamically typed language, you don't care what type of list it is, just that it's a list. However, in a statically typed language, you do care what type of list it is because the type is "a list of A" where "A" is some type. That is, a list A
is a different type from list B
.
So when you speak of generics, calling some function of type A -> B
each item of a list with a foreach
means that the list must be a list A
. But... if you use generics, then you don't have to declare what A
is, you can just have it be filled in at a later date. Thus, you establish the contract whereby given a list C
and a function A -> B
, A === C
in order for it to compile. This reduces boilerplate considerably.
In Go, given the lack of generics and the ability to declare such a type contract, you have to write a function that operates on a list of int, a list of double, a list of string, etc. You can't just define things in a "generic" manner.

- 35,646
- 15
- 94
- 131
-
Not exactly true as another common approach, where possible, is to write a generic (in a colloquial sense) implementation operating on instances of the `interface{}` type (which matches any type at all) and then use the so-called type assertion to "acquire" type-safe objects back from the container/function. – kostix Jan 18 '14 at 12:01
-
It's more coarse-grained approach than true generics but in exchange it allows to not bring into the language all the complexities and subtleties required to sensibly support generics; and it's *this* trade-off which is being discussed most. – kostix Jan 18 '14 at 12:03
-
@kostix Of course you can do that. The problem isn't with not getting objects of the right type out, but with not getting objects of the right type in though. Note that a type assertion also has some runtime overhead. I'd personally say go doesn't really need generics, but I'd also say if it ever does we might as well go the extra mile and make it templates instead of java-esque generics. – Cubic Jan 18 '14 at 13:07
-
It's probably worth pointing out that Go *does* have generics, for a few built in types: arrays, slices, maps, and channels. It just isn't possible to add generics to your own custom types. – Tyler Jan 20 '14 at 05:58
Update Q1 2022
Generics are officially supported with Go 1.18
Go 1.18 includes an implementation of generic features as described by the Type Parameters Proposal.
This includes major - but fully backward-compatible - changes to the language.
See nwillc/genfuncs
for (lots of) examples:
// Keys returns a slice of all the keys in the map.
func Keys[K comparable, V any](m map[K]V) []K {
keys := make([]K, len(m))
var i int
for k, _ := range m {
keys[i] = k
i++
}
return keys
}
2014:
William B. Yager blog post reminds why the "generic" part present in Go is not enough:
You can write generic functions easily enough.
Let's say you wanted to write a function that printed a hash code for objects that could be hashed. You can define an interface that allows you to do this with static type safety guarantees, like this:
type Hashable interface {
Hash() []byte
}
func printHash(item Hashable) {
fmt.Println(item.Hash())
}
Now, you can supply any
Hashable
object toprintHash
, and you also get static type checking. This is good.
What if you wanted to write a generic data structure?
Let's write a simple Linked List. The idiomatic way to write a generic data structure in Go is:
(here is just the start)
type LinkedList struct {
value interface{}
next *LinkedList
}
func (oldNode *LinkedList) prepend(value interface{}) *LinkedList {
return &LinkedList{value, oldNode}
}
The "correct" way to build generic data structures in Go is to cast things to the top type and then put them in the data structure. This is how Java used to work, circa 2004. Then people realized that this completely defeated the purpose of type systems.
When you have data structures like this, you completely eliminate any of the benefits that a type system provides. For example, this is perfectly valid code:
node := tail(5).prepend("Hello").prepend([]byte{1,2,3,4})
So that is why, if you want to retain the benefit of type system, you have to use some code generation, to generate the boileplate code for your specific type.
The gen
project is an example of that approach:
gen
generates code for your types, at development time, using the command line.
gen
is not an import; the generated source becomes part of your project and takes no external dependencies.
Update June 2017: Dave Cheney detailed what Generics for Go would mean in his articles "Simplicity Debt" and "Simplicity Debt Redux".
Since Go 2.0 is now actively discussed at the core team level, Dave points out what Generics involve, and that is:
- Error handling: Generic would allow a monadic Error handling, meaning you need to understand monad on top of the rest: that enable handling computational pipeline instead of checking errors after each function call.
But: you need to understand monad! - Collections: facilitate custom collection types without the need for interface{} boxing and type assertions.
But that leaves the question of what to do with the built in slice and map types. - Slicing: Does it go away, if so, how would that impact common operations like handling the result a call to
io.Reader.Read
?
If slicing doesn’t go away, would that require the addition of operator overloading so that user defined collection types can implement a slice operator? - Vector: Go’s Pascal-like array type has a fixed size known at compile time. How could you implement a growable vector without resorting to unsafe hacks?
- Iterator: what you really want to be able to do is compose iterators over database results and network requests.
In short, data from outside your process—and when data is outside your process, retrieving it might fail.
In that case you have a choice, does yourIterable
interface return a value, a value and an error, or perhaps you go down the option type route. - Immutability: The ability to mark a function parameter as
const
is insufficient, because while it restricts the receiver from mutating the value, it does not prohibit the caller from doing so, which is the majority of the data races I see in Go programs today.
Perhaps what Go needs is not immutability, but ownership semantics.
As Russ Cox writes in "My Go Resolutions for 2017":
Today, there are newer attempts to learn from as well, including Dart, Midori, Rust, and Swift.
The latest discussion is Go issue 15292: it also references "Summary of Go Generics Discussions".

- 1,262,500
- 529
- 4,410
- 5,250
Go 1.18 introduced generics (beta release)
You don't have to be left wondering now. Generics are a reality in Go. The draft release notes of Go 1.18 officially announce the introduction of type parameters into the language in a backward-compatible way.
The current set of specifications for type parameters can be found in the type parameters proposal authored by Ian Lance Taylor and Robert Griesemer.
These specs are already implemented into the language at tip, which you can run right now on the Gotip Playground.
A very high level overview
(source)
- Functions can have an additional type parameter list that uses square brackets but otherwise looks like an ordinary parameter list:
func F[T any](p T) { ... }
.- These type parameters can be used by the regular parameters and in the function body.
- Types can also have a type parameter list:
type M[T any] []T
.- Each type parameter has a type constraint, just as each ordinary parameter has a type:
func F[T Constraint](p T) { ... }
.- Type constraints are interface types.
- The new predeclared name
any
is a type constraint that permits any type.- Interface types used as type constraints can embed additional elements to restrict the set of type arguments that satisfy the constraint:
- an arbitrary type
T
restricts to that type- an approximation element
~T
restricts to all types whose underlying type isT
- a union element
T1 | T2 | ...
restricts to any of the listed elements
- Generic functions may only use operations supported by all the types permitted by the constraint.
- Using a generic function or type requires passing type arguments.
- Type inference permits omitting the type arguments of a function call in common cases.
Example of a generic function
func echo[T any](v T) T {
return v
}
Note that any
is a type alias of interface{}
Example of a generic struct type
type Foo[T any] struct {
val T
}
Example of an interface constraint with type set and approximation types
type FloatingPoint interface {
~float32 | ~float64
}
Interface constraints can also be parametrized, for example a constraint that restricts any type to its pointer counterpart:
type Ptr[T any] interface {
*T
}

- 34,072
- 23
- 111
- 129