Skip to content
Go

Go 1.27 Preview: The Arrival of Generic Methods and Improved Tooling

Published: Duration: 6:52
0:00 0:00

Transcript

Host: Alex Chan Hey everyone, welcome back to Allur, your go-to spot for everything happening in the worlds of PHP, Laravel, Go, and mobile dev. I’m your host, Alex Chan. Host: Alex Chan Joining me today is Marcus Thorne. Marcus is a Principal Engineer at CloudScale and a long-time contributor to the Go project. He’s been following the generic methods proposal since it was just a "maybe" on a GitHub issue years ago. Marcus, thanks so much for hopping on Allur! Guest: Marcus Thorne Thanks for having me, Alex! Yeah, "years ago" is right. I think some of us have been dreaming about this since before 1.18 even dropped. It’s a very exciting time to be a Gopher. Host: Alex Chan So, let's start with the "why." For those who might be listening and thinking, "Wait, don’t we already have generics?", what exactly was the "receiver barrier" we were dealing with before 1.27? Guest: Marcus Thorne Right, so, in Go 1.18 through 1.26, you could make a *struct* generic. Like, you could have a `List`. But the limitation was that any method on that struct could only use that specific `T`. If you wanted a method to introduce a *new* type—say, you wanted a `Map` function that turns your `List` into a `List`—you couldn't do that as a method. Host: Alex Chan Because the method was stuck with whatever the struct was defined as? Guest: Marcus Thorne Exactly. The receiver—that part in the parentheses before the function name—was the boss. If the receiver didn't know about the type, the method couldn't either. This is why we saw the standard library grow all these package-level functions. We had to write `slices.Map(mySlice, transformFunc)` instead of just saying `mySlice.Map(transformFunc)`. It felt very... un-object-oriented? It broke that "fluent" flow where you can just chain operations together. It led to what I call "helper-heavy" codebases where you’re constantly jumping out of your object logic to call a utility function. Host: Alex Chan I’ve definitely felt that. It always felt a bit jarring to be mid-chain and then have to wrap the whole thing in a package function. So, how does 1.27 change the syntax? Is it pretty intuitive? Guest: Marcus Thorne It’s actually super clean. If you know how to write a generic function, you basically already know how to write a generic method. You just put the type parameter list—you know, the square brackets—right after the method name but before the arguments. So, if you have a `Registry` struct, you can now have a method like `Register(name string, value T)`. The `Registry` itself doesn't have to be generic, but that specific *action* of registering can handle any type you throw at it. Host: Alex Chan That sounds... almost too simple? I mean, if it’s that easy, why did it take us nearly ten releases to get here? Guest: Marcus Thorne Oh man, the "why" is where it gets nerdy. The implementation was a nightmare for the compiler team. In Go, we have interfaces. An interface is basically a contract of methods. If a method can be generic, it means that method could potentially represent an *infinite* number of signatures. Host: Alex Chan Wait, infinite? Because `T` could be anything? Guest: Marcus Thorne Exactly! So, how does the compiler build a "vtable"—the look-up table for these methods—when it doesn't know how many versions of that method will exist at runtime? If you have an interface with a generic method, how do you prove a struct satisfies that interface? The Go team had to figure out a way to handle this without tanking performance. Host: Alex Chan And I'm assuming they didn't want to sacrifice that "pay only for what you use" philosophy that Go is famous for? Guest: Marcus Thorne Precisely. What they landed on for 1.27 is this really clever hybrid approach. It uses monomorphization—which is a fancy word for generating specific code for the types it *knows* about at compile time—but it also uses runtime dictionaries for the more dynamic cases. It’s sophisticated, but as a developer, you don't really see it. You just get the flexibility without the binary bloat or the speed hit. Host: Alex Chan That’s honestly impressive. So, let’s talk about the impact on library design. You mentioned "fluent APIs" earlier. How is this going to change the way we actually build things? Guest: Marcus Thorne It’s going to be a revolution for library maintainers. Think about a data transformation pipeline. Before, you’d be casting things to `interface{}`—or `any`, as we call it now—and then doing type assertions, which is just a recipe for runtime panics. Now, we can build these beautiful, type-safe Builders. You could have a stream that says `.Map(parse)`.`.Filter(isValid)`.`.Collect()`. And the whole time, the compiler is checking your types. Host: Alex Chan So no more "fingers crossed" when you run your transformation logic? Guest: Marcus Thorne Exactly! Actually, I think we'll see a lot of the older "magic" libraries—like dependency injection containers or heavy serialization frameworks that rely on reflection and `any`—get a total overhaul. If you can replace a runtime check with a compile-time constraint, you’ve just made your system ten times more reliable. Host: Alex Chan I love that. But I’m thinking about all the codebases I work on that are... well, they’re old. They’re full of those package-level helpers we talked about. Moving all of that to this new method-based style sounds like a massive headache. Does 1.27 give us any help there? Guest: Marcus Thorne It actually does! This is the part I’m arguably most excited about. Go is introducing these new "modernizer" tools within the `go fix` command. Host: Alex Chan Wait, so `go fix` is getting an upgrade specifically for this? Guest: Marcus Thorne Yeah! It’s not just a find-and-replace tool. These modernizers do real static analysis. They can look at your codebase, identify those "pre-generic" patterns—like those helper functions we mentioned—and actually suggest how to turn them into generic methods. It even checks for things like circular dependencies or visibility issues before it makes the change. It’s designed to make the transition incremental. You don’t have to rewrite your whole API overnight. You can run the modernizer on your internal utilities first and see how it feels. Host: Alex Chan That is so "Go." I’ve always appreciated how the core team doesn't just throw new features at us and say "good luck." They actually give us the tools to manage the technical debt that comes with it. Guest: Marcus Thorne Right? And the compiler's type inference is getting better too. Most of the time, you won’t even have to write the square brackets when you *call* the method. The compiler just looks at your arguments and says, "Oh, I see, you’re passing an int here, I’ll handle it." It keeps the code looking clean. Host: Alex Chan So, for someone listening who’s ready to jump into the 1.27 preview, what’s your best piece of advice for getting started with generic methods? Guest: Marcus Thorne I’d say: don't over-engineer it on day one. It’s tempting to go back and make *everything* a generic method. But the Go philosophy is still "keep it simple." Start by looking at your transformation logic or your collection types. If you find yourself frequently jumping out of a method chain to use a `slices` or `maps` package function, that’s your prime candidate. And definitely play with the new `go fix` modernizers. See what they suggest—it’s a great way to learn the new patterns. Host: Alex Chan "Keep it simple"—the golden rule of Go. Marcus, this has been incredibly enlightening. I think a lot of people are going to be diving into those docs as soon as they finish this episode. Guest: Marcus Thorne I hope so! It’s a huge step forward for the language. Thanks for having me, Alex. Host: Alex Chan Of course!

Tags

Go Golang software engineering modernization generics compiler