Go 1.27 Interactive Tour

338 points170 comments19 hours ago
YesThatTom2

"The best way to teach something new is to compare it to something the audience already understands."

Could someone take the example, reduce it to a non-generic version for two types I DO understand, then show that with the new feature I can collapse them into the Box/Map example in the doc?

I have 10+ years of Go experience and I can't make heads or tails of "(b Box[T]) Map[U any](f func(T) U) Box[U]"

show comments
baalimago

This: "(b Box[T]) Map[U any](f func(T) U) Box[U]" is the type of cognitive weight I was happy that Go avoided.

show comments
chenxiaolong

This release also fixes runtime.findnull() to be compatible with MTE on Android ([1] and [2]). This was the only thing preventing MTE from being enabled for apps that use gomobile on MTE-compatible Android OS's like GrapheneOS.

[1] https://go-review.googlesource.com/c/go/+/749062

[2] https://go-review.googlesource.com/c/go/+/751020

show comments
mappu

Automatically draining http response bodies is a risky silent behaviour change. I think it will be an improvement for most applications, but it's very subtle if you were relying on the old behaviour

show comments
sbstp

Go's standard library has always been it's strength, especially the crypto package! Lovely stuff.

my-next-account

>The quieter but bigger change

I really wish they didn't use such stupid LLM-isms.

show comments
nu2ycombinator

Those Generics syntax in Golang seems so hard to read.

show comments
freecodyx

“Go 1.27 is a meaty release whose center of gravity is the type system. A few themes stand out:”

llm generated

mayama

Adding simd in std and even being used in map is nice. Would have to look for places to experiment with it in hot loops in code I have.

ewy1

fascinating how many people are displeased with the expansion of generics! i love go and this is some functionality i always missed.

Hixon10

Some examples for the upcoming release https://go.dev/doc/go1.27

neild

Many comments on generic methods. Perhaps this example will help understand a hopefully not-too-objectionable case of using them in practice.

The math/rand/v2 package has a number of functions which return random numbers of a certain type:

    i := rand.Int32()  // a random signed 32-bit integer (type int32)
    j := rand.Uint64() // a random unsigned 64-bit integer (type uint64)
It has functions which return a number within a range:

    in := rand.Int32N(10)   // a random int32 in the range [0,10)
    jn := rand.Uint64N(100) // a random uint64 in the range [0,100)
It also has a generic function, rand.N, where the return type is set by a type parameter. The definition of rand.Int32N (for comparison) and rand.N are:

    func Int32N(n int32) int32
    func N[Int intType] (n Int) Int
Adding some spaces to make the common elements align (apologies if my formatting gets mangled), that's:

    func Int32N               (n int32) int32
    func N      [Int intType] (n Int  ) Int
As you can see, the generic function N has the same signature as the non-generic Int32N, except the type it operates on is set by a type parameter (named "Int"). The type parameter has a constraint, intType, which is a private type defined in the math/rand package. (There's nothing magic about this constraint, it's just a list of all the integer types in the language, and you can write it yourself if you want to. It's a separate type to keep the function signature of N from becoming too large, and it's internal to math/rand because it doesn't need to be part of the public package API.)

The nice thing about rand.N is that it lets you write something like this:

    // d is a random time.Duration in the range [0, 10 minutes)
    d := rand.N(10 * time.Minute)
Without generics, you'd instead write this as the following, which is a lot more noise:

    d := time.Duration(rand.Int64N(int64(10 * time.Minute)))
The generic rand.N has a more confusing type signature and a lot more language complexity behind it, but the code using it is simpler and easier to read. We think that's a good tradeoff, but of course not everyone will agree.

All the functions I've mentioned so far use a default random number source. Each of them also exists as a method of the rand.Rand type, which generates numbers from a user-provided randomness source. For example:

    rng := rand.New(rand.NewChaCha8(seed))
    a := rng.Uint64()
    b := rng.Uint64N(100)
There is one exception, though: Until Go 1.27, there was no Rand.N method, because we did not support generic methods. (A generic type could have methods, but those methods could not be further type parameterized.)

In Go 1.27, there is now a Rand.N method:

    // Using a ChaCha8-based source with a defined seed,
    // generate a duration in the range [0, 10 minutes).
    rng := rand.New(rand.NewChaCha8(seed))
    d := rng.Duration(10 * time.Minute)
This method's signature is:

    func (r *Rand) N[Int intType](n Int) Int
Comparing function vs. method and generic vs. concrete:

    func           Int32N               (n int32) int32 // function
    func           N      [Int intType] (n Int  ) Int   // generic function
    func (r *Rand) Int32N               (n int32) int32 // method
    func (r *Rand) N      [Int intType] (n Int)   Int   // generic method
In this case, generic methods permit us to fix a small wart in the package API. This example isn't the motivating reason for adding generic methods, but I think it serves as an example of how adding them makes the language a bit simpler and more consistent. In Go, methods are just a type of function. Previously, you could write a type-parameterized function, but you couldn't write a type-parameterized method. That's an inconsistency that you need to remember. Now you can write type-parameterized functions or methods, using a consistent syntax for either.

Type-parameterized methods don't participate in interface satisfaction, so this change isn't without its own subtleties. Discussing the tradeoffs there would double the length of this post, and weighing them is why it took so long for us to decide to add generic methods.

Another possibility is that people will use generic methods to write unreadably complex code. My personal opinion is that nothing will stop people from writing unreadably complex code if they want to; the fix to complexity is to not do that.

lilbigdoot

This level of generics actually has me interested a bit in Go now.

show comments
fweimer

> interfaces still can’t declare type-parameterized methods

What would an implementation look like? Wouldn't it be quite different from the existing one because it has to rely heavily on indirection because (limited) monomorphimization is not possible?

show comments
KolmogorovComp

Instead of having each language bring progress in a different and/or novel, we get this, old java features that comes 20 years-in after the making.

They're probably useful, but clearly not sexy (as golang in general).

drivebyhooting

Can generics be used to improve error handling and eliminate the if err pattern?

show comments
theplumber

This is quite of a big release and I like the new methods on generics.

show comments
Altern4tiveAcc

>func (b Box[T]) Map[U any](f func(T) U) Box[U] {}

That's completely unreadable.

show comments
rednafi

I am all for using LLMs to generate value but a little more editorial review can't hurt.

> The quieter but bigger change: the classic encoding/json (v1) package is now backed by the v2 implementation under the hood.

This is fantastic content nevertheless.

nothrows

generics were a slippery slope. give it a decade and Go will be indistinguishable from c++

show comments
stingraycharles

Am I the only one who’s absolutely shocked that Go finally is embracing generics?

Does anyone have a bit of an inside view into what changed in the perspectives of the language maintainers?

I’m not buying the “it took us 20 years to understand how to do it correctly” argument, as this is something you explicitly take into consideration when designing the language or not. And it was specifically not a part of language design, and is much harder to retrofit (backwards compatibility).

So what changed?

show comments
cat-whisperer

does go have enums?

show comments
kansm

Tried running a couple of examples in the tour, but ran into a few errors.

show comments
pixxxel

Let's GO

adrianmsmith

One thing I think generics in Go is missing is the <?> concept in Java.

If you're taking a List[T] and all you want to do is to call list.size() then you don't care what type of list it is. In Java you can write a function which takes a List<?> but in Go you have to write List[T] so then the question becomes what is T? You have to make the function (or type you're a method on) generic. If you make the type generic then every user of your type also needs to specify T, etc.

I don't think it would be impossible to add that to Go. Allow List[?], which matches a List with any type parameter. Calling functions which don't involve the type parameter like list.size() would be fine, calling a method returning the type parameter like list.get(n) would return "any", and methods taking the type parameter like list.set(n, obj) would probably not be callable.

show comments