Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Early Perspectives on Go Generics
Matt Dale
Matt Dale

Posted on • Edited on

     

Early Perspectives on Go Generics

Go 1.18 is scheduled for release sometime soon (originally Feb 2022) and will be the first version of Go to supportgeneric programming, commonly called "generics". The lack of generics in Go has been a contentious topic for a long time, as Ian Lance Taylor pointed out in his GopherCon presentation in 2019:

Go was released November 10, 2009. Less than 24 hours later, we saw the first comment about generics.
— Ian Lance Taylor,GopherCon 2019: Generics in Go

There are already lots of goodarticles,tutorials, andtalks about generics in Go, so this article will just focus on the experience of writing and using generic code in Go.


Disclaimer: Go 1.18 is not released yet! These early perspectives on Go generics are using the latestgotip version as of Feb 21, 2022. Some of the behaviors described in this article may change in the final release.


What's the Big Deal with Generics?

Before we get into what using generics in Go is like, let's take a step back and ask, "Why do developers use generics and what improvements do generics offer?" Let's turn to a quote by Alexander Stepanov, original designer of theC++ Standard Template Library, for help:

Generic programming is about abstracting and classifying algorithms and data structures ... Its goal is the incremental construction of systematic catalogs of useful, efficient and abstract algorithms and data structures.
— Alexander Stepanov,Short History of STL

So generic programming is primarily about being able to focus on writing code once and using it many times with many types. How does it work in Go?

Go Generics: The Really Really Short Version

Go generics adds a "type constraint" syntax that allows functions and types to specify multiple types that they can receive and return. Let's make that more concrete with a simpleMin function that accepts two numeric values and returns the minimum value of the two. OurMin function can accept two parameters, both either anint64 orfloat64, and returns a value the same type as the inputs.

funcMin[Tint64|float64](x,yT)T{ifx<y{returnx}returny}
Enter fullscreen modeExit fullscreen mode

To call our genericMin function, we call it with values that satisfy the type constraint,int64 in this case. The Go compiler infers appropriate types, resulting in code that looks the same as non-generic Go code.

varxint64=5varyint64=12Min(x,y)// returns 5
Enter fullscreen modeExit fullscreen mode

For a more in-depth look at generics in Go, check out the(beta) Go generics tutorial.

Early Perspectives

Here's what you came for, some selected early perspectives on using Go generics!

Generic Type Inference

Type inference is a concept already familiar to most Go developers. When you assign a value to a variable using the:= assignment operator, you're asking the Go compiler to infer the correct variable type. Go generics use a similar concept to try to determine the correct types to use when calling a generic function.

The good news is type inference works great with function parameters! Calling functions that use generic input parameter types is very intuitive in most cases, and is often syntactically identical to calling non-generic functions.

However, calling functions with a generic return type often requires you to specify the type in the call. For example, let's consider the following generic function that returns a positive infinity floating point value as afloat32 orfloat64:

funcInf[Tfloat32|float64](t)T{returnT(math.Inf(1))}
Enter fullscreen modeExit fullscreen mode

Now let's try to call theInf function to assign a+Inf value to afloat64 variable:

varxfloat64x=Inf()
Enter fullscreen modeExit fullscreen mode

Oops, we got a compile error!

./main.go:20:15: cannot infer T (./main.go:10:10)
Enter fullscreen modeExit fullscreen mode

Instead, we have to specify the function type explicitly:

varxfloat64x=Inf[float64]()
Enter fullscreen modeExit fullscreen mode

As a result, calling generic functions where the Go compiler can't use the input parameters to infer the return type can be somewhat awkward. There's an open discussion about whether or not Go should support generic function type inference when assigning to a variablehere, but for now you have to declare the return type explicitly.

APIs and Type Switches

Generics also gives us new options when we want to add functionality to APIs while maintaining backward compatibility. Consider a functionDo that takes anint value and performs some action.

funcDo(xint){// Perform some action with int x.}
Enter fullscreen modeExit fullscreen mode

Now let's say we wantDo to support either anint or abool, and perform a different action for with each type. Before generics, we might have just added new functionsDoInt andDoBool, and kept theDo function to maintain API backward compatibility.

funcDoInt(xint){// Perform some action with int x.}funcDoBool(xbool){// Perform some action with bool x.}funcDo(xint){DoInt(x)}
Enter fullscreen modeExit fullscreen mode

With generics, we can add a type constraint to ourDo function to accept either anint or abool and use a type switch to do the appropriate action for each type.

funcDo[Tint|bool](xT){switchany(x).(type){caseint:// Perform some action with int x.casebool:// Perform some action with bool x.}}
Enter fullscreen modeExit fullscreen mode

Note:any is a new shorthand forinterface{}.

We've reduced our API surface area by only using a singleDo function, and maintained API backward compatibility in most cases (calls toDo using reflection may still be impacted), but the resulting implementation code is less clear than two separate functions. Additionally, there's no compile-time check to ensure that the type constraint and switch cases match, so they may get out-of-sync and lead to bugs.

Whether or not it's a good idea to update APIs to accept more types using type constraints is still unclear. There is an opendiscussion about amending the type switch to work more gracefully with type parameters, so follow that if you're interested in the outcome!

Testing

With more types come more tests! If your functions only support a single type, then you only need to test with values of a single type. Once your functions support many types, you need to test with values of every supported type. That can balloon into lots of test cases quickly!

To complicate things, some common testing practices won't work when using generics. For example, anonymous functions and closures cannot use type parameters (check out the discussionhere).

As a result, you may end up with quite large tests with lots of subtests and lots of test cases, like the oneshere. We'll need to develop new testing best practices to handle the new challenges that come with testing generic code in Go.

Wrapping Up

Go generics will be available soon and may significantly change how we write Go. There is a lot to learn about the best ways to use Go generics and we will undoubtedly make plenty of mistakes along the way. Considering that, Rob Pike'ssuggestion to keep generics out of most of the standard library for the Go 1.18 release seems very wise.

Finally, here's a plug for thegmath library, my attempt to write generic versions of commonly used functions from the Gomath package. Check it out if you needmath functions for numeric types other thanfloat64!

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Software engineer, working on Go OSS and MongoDB, trying out blogging, and living life in the PNW.
  • Location
    Bellevue, WA
  • Pronouns
    he/him
  • Work
    MongoDB
  • Joined

More fromMatt Dale

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp