By using this site, you agree to the Privacy Policy and Terms of Use.
Accept
World of SoftwareWorld of SoftwareWorld of Software
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Search
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
Reading: Go: When Should You Use Generics? When Shouldn’t You? | HackerNoon
Share
Sign In
Notification Show More
Font ResizerAa
World of SoftwareWorld of Software
Font ResizerAa
  • Software
  • Mobile
  • Computing
  • Gadget
  • Gaming
  • Videos
Search
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Have an existing account? Sign In
Follow US
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
World of Software > Computing > Go: When Should You Use Generics? When Shouldn’t You? | HackerNoon
Computing

Go: When Should You Use Generics? When Shouldn’t You? | HackerNoon

News Room
Last updated: 2025/10/04 at 2:52 PM
News Room Published 4 October 2025
Share
SHARE

Introduction

This is the blog post version of my talks at Google Open Source Live:

and GopherCon 2021:

https://youtu.be/Pae9EeCdy8?si=M-87Eisb2nU1qmJ&embedable=true

The Go 1.18 release adds a major new language feature: support for generic programming. In this article I’m not going to describe what generics are nor how to use them. This article is about when to use generics in Go code, and when not to use them.

To be clear, I’ll provide general guidelines, not hard and fast rules. Use your own judgement. But if you aren’t sure, I recommend using the guidelines discussed here.

Write code

Let’s start with a general guideline for programming Go: write Go programs by writing code, not by defining types. When it comes to generics, if you start writing your program by defining type parameter constraints, you are probably on the wrong path. Start by writing functions. It’s easy to add type parameters later when it’s clear that they will be useful.

When are type parameters useful?

That said, let’s look at cases for which type parameters can be useful.

When using language-defined container types

One case is when writing functions that operate on the special container types that are defined by the language: slices, maps, and channels. If a function has parameters with those types, and the function code doesn’t make any particular assumptions about the element types, then it may be useful to use a type parameter.

For example, here is a function that returns a slice of all the keys in a map of any type:

// MapKeys returns a slice of all the keys in m.
// The keys are not returned in any particular order.
func MapKeys[Key comparable, Val any](m map[Key]Val) []Key {
    s := make([]Key, 0, len(m))
    for k := range m {
        s = append(s, k)
    }
    return s
}

This code doesn’t assume anything about the map key type, and it doesn’t use the map value type at all. It works for any map type. That makes it a good candidate for using type parameters.

The alternative to type parameters for this kind of function is typically to use reflection, but that is a more awkward programming model, is not statically typechecked at build time, and is often slower at run time.

General purpose data structures

Another case where type parameters can be useful is for general purpose data structures. A general purpose data structure is something like a slice or map, but one that is not built into the language, such as a linked list, or a binary tree.

Today, programs that need such data structures typically do one of two things: write them with a specific element type, or use an interface type. Replacing a specific element type with a type parameter can produce a more general data structure that can be used in other parts of the program, or by other programs. Replacing an interface type with a type parameter can permit data to be stored more efficiently, saving memory resources; it can also permit the code to avoid type assertions, and to be fully type checked at build time.

For example, here is part of what a binary tree data structure might look like using type parameters:

// Tree is a binary tree.
type Tree[T any] struct {
    cmp  func(T, T) int
    root *node[T]
}

// A node in a Tree.
type node[T any] struct {
    left, right  *node[T]
    val          T
}

// find returns a pointer to the node containing val,
// or, if val is not present, a pointer to where it
// would be placed if added.
func (bt *Tree[T]) find(val T) **node[T] {
    pl := &bt.root
    for *pl != nil {
        switch cmp := bt.cmp(val, (*pl).val); {
        case cmp < 0:
            pl = &(*pl).left
        case cmp > 0:
            pl = &(*pl).right
        default:
            return pl
        }
    }
    return pl
}

// Insert inserts val into bt if not already there,
// and reports whether it was inserted.
func (bt *Tree[T]) Insert(val T) bool {
    pl := bt.find(val)
    if *pl != nil {
        return false
    }
    *pl = &node[T]{val: val}
    return true
}

Each node in the tree contains a value of the type parameter T. When the tree is instantiated with a particular type argument, values of that type will be stored directly in the nodes. They will not be stored as interface types.

This is a reasonable use of type parameters because the Tree data structure, including the code in the methods, is largely independent of the element type T.

The Tree data structure does need to know how to compare values of the element type T; it uses a passed-in comparison function for that. You can see this on the fourth line of the find method, in the call to bt.cmp. Other than that, the type parameter doesn’t matter at all.

For type parameters, prefer functions to methods

The Tree example illustrates another general guideline: when you need something like a comparison function, prefer a function to a method.

We could have defined the Tree type such that the element type is required to have a Compare or Less method. This would be done by writing a constraint that requires the method, meaning that any type argument used to instantiate the Tree type would need to have that method.

A consequence would be that anybody who wants to use Tree with a simple data type like int would have to define their own integer type and write their own comparison method. If we define Tree to take a comparison function, as in the code shown above, then it is easy to pass in the desired function. It’s just as easy to write that comparison function as it is to write a method.

If the Tree element type happens to already have a Compare method, then we can simply use a method expression like ElementType.Compare as the comparison function.

To put it another way, it is much simpler to turn a method into a function than it is to add a method to a type. So for general purpose data types, prefer a function rather than writing a constraint that requires a method.

Implementing a common method

Another case where type parameters can be useful is when different types need to implement some common method, and the implementations for the different types all look the same.

For example, consider the standard library’s sort.Interface. It requires that a type implement three methods: Len, Swap, and Less.

Here is an example of a generic type SliceFn that implements sort.Interface for any slice type:

// SliceFn implements sort.Interface for a slice of T.
type SliceFn[T any] struct {
    s    []T
    less func(T, T) bool
}

func (s SliceFn[T]) Len() int {
    return len(s.s)
}
func (s SliceFn[T]) Swap(i, j int) {
    s.s[i], s.s[j] = s.s[j], s.s[i]
}
func (s SliceFn[T]) Less(i, j int) bool {
    return s.less(s.s[i], s.s[j])
}

For any slice type, the Len and Swap methods are exactly the same. The Less method requires a comparison, which is the Fn part of the name SliceFn. As with the earlier Tree example, we will pass in a function when we create a SliceFn.

Here is how to use SliceFn to sort any slice using a comparison function:

// SortFn sorts s in place using a comparison function.
func SortFn[T any](s []T, less func(T, T) bool) {
    sort.Sort(SliceFn[T]{s, less})
}

This is similar to the standard library function sort.Slice, but the comparison function is written using values rather than slice indexes.

Using type parameters for this kind of code is appropriate because the methods look exactly the same for all slice types.

(I should mention that Go 1.19–not 1.18–will most likely include a generic function to sort a slice using a comparison function, and that generic function will most likely not use sort.Interface. See proposal #47619. But the general point is still true even if this specific example will most likely not be useful: it’s reasonable to use type parameters when you need to implement methods that look the same for all the relevant types.)

When are type parameters not useful?

Now let’s talk about the other side of the question: when not to use type parameters.

Don’t replace interface types with type parameters

As we all know, Go has interface types. Interface types permit a kind of generic programming.

For example, the widely used io.Reader interface provides a generic mechanism for reading data from any value that contains information (for example, a file) or that produces information (for example, a random number generator). If all you need to do with a value of some type is call a method on that value, use an interface type, not a type parameter. io.Reader is easy to read, efficient, and effective. There is no need to use a type parameter to read data from a value by calling the Read method.

For example, it might be tempting to change the first function signature here, which uses just an interface type, into the second version, which uses a type parameter.

func ReadSome(r io.Reader) ([]byte, error)

func ReadSome[T io.Reader](r T) ([]byte, error)

Don’t make that kind of change. Omitting the type parameter makes the function easier to write, easier to read, and the execution time will likely be the same.

It’s worth emphasizing the last point. While it’s possible to implement generics in several different ways, and implementations will change and improve over time, the implementation used in Go 1.18 will in many cases treat values whose type is a type parameter much like values whose type is an interface type. What this means is that using a type parameter will generally not be faster than using an interface type. So don’t change from interface types to type parameters just for speed, because it probably won’t run any faster.

Don’t use type parameters if method implementations differ

When deciding whether to use a type parameter or an interface type, consider the implementation of the methods. Earlier we said that if the implementation of a method is the same for all types, use a type parameter. Inversely, if the implementation is different for each type, then use an interface type and write different method implementations, don’t use a type parameter.

For example, the implementation of Read from a file is nothing like the implementation of Read from a random number generator. That means that we should write two different Read methods, and use an interface type like io.Reader.

Use reflection where appropriate

Go has run time reflection. Reflection permits a kind of generic programming, in that it permits you to write code that works with any type.

If some operation has to support even types that don’t have methods (so that interface types don’t help), and if the operation is different for each type (so that type parameters aren’t appropriate), use reflection.

An example of this is the encoding/json package. We don’t want to require that every type that we encode have a MarshalJSON method, so we can’t use interface types. But encoding an interface type is nothing like encoding a struct type, so we shouldn’t use type parameters. Instead, the package uses reflection. The code is not simple, but it works. For details, see the source code.

One simple guideline

In closing, this discussion of when to use generics can be reduced to one simple guideline.

If you find yourself writing the exact same code multiple times, where the only difference between the copies is that the code uses different types, consider whether you can use a type parameter.

Another way to say this is that you should avoid type parameters until you notice that you are about to write the exact same code multiple times.


Ian Lance Taylor

This article is available on The Go Blog under a CC BY 4.0 DEED license.

Photo by Michael Dziedzic on Unsplash

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Twitter Email Print
Share
What do you think?
Love0
Sad0
Happy0
Sleepy0
Angry0
Dead0
Wink0
Previous Article Don’t Miss Out: 10 Big Video Game Releases You’ll Want to Play in October
Next Article Can You Legally Share ICE Raids on Apps Like Ring Neighbors or Nextdoor?
Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Stay Connected

248.1k Like
69.1k Follow
134k Pin
54.3k Follow

Latest News

Putin’s Moldova election failure highlights Russia’s declining influence
News
SMIC boosts domestic chip equipment use amid China’s tech independence drive · TechNode
Computing
Today's NYT Connections Hints, Answers for Oct. 5 #847
News
I built my own sync system without Google Drive or OneDrive and it works flawlessly
Computing

You Might also Like

Computing

SMIC boosts domestic chip equipment use amid China’s tech independence drive · TechNode

1 Min Read
Computing

I built my own sync system without Google Drive or OneDrive and it works flawlessly

9 Min Read
Computing

Digital Discrimination: Los Angeles Becomes First City in the U.S. to Ban It | HackerNoon

7 Min Read
Computing

TikTok eyes a big step on US shopping business sales for second-half amid current disappointing performance: report · TechNode

3 Min Read
//

World of Software is your one-stop website for the latest tech news and updates, follow us now to get the news that matters to you.

Quick Link

  • Privacy Policy
  • Terms of use
  • Advertise
  • Contact

Topics

  • Computing
  • Software
  • Press Release
  • Trending

Sign Up for Our Newsletter

Subscribe to our newsletter to get our newest articles instantly!

World of SoftwareWorld of Software
Follow US
Copyright © All Rights Reserved. World of Software.
Welcome Back!

Sign in to your account

Lost your password?