Sponsored Content

DEV Community

Cover image for Go 1.27 Generic Methods: A Cheat Sheet for Functions, Types & Methods
jjpinto
jjpinto

Posted on

Go 1.27 Generic Methods: A Cheat Sheet for Functions, Types & Methods

๐Ÿงฉ Part 3 of the Idiomatic Go Series โ€” see Part 1: The Power of Idiomatic Go and Part 2: Go Naming Cheat Sheet

Generics in Go have come a long way since 1.18. Type inference got smarter, the standard library absorbed slices, maps, and cmp, and call-site noise mostly disappeared. Now, with Go 1.27, the final major piece lands, and it's the headline language change of the release: generic methods, methods that can declare their own standalone type parameters, even when the receiver type is not generic.

This cheat sheet covers all three levels of Go generics โ€” functions, types, and generic methods โ€” with practical examples, real-world stdlib patterns, and key gotchas to keep in mind.


Quick Reference Card

These three patterns form the backbone of Goโ€™s new expressive power โ€” without sacrificing simplicity.

// 1. Generic Function
func PrintAnything[T any](v T) { fmt.Println(v) }

// 2. Generic Type
type Box[T any] struct { item T }

// 3. Generic Method (New in Go 1.27)
func (r Result[T]) Transform[U any](f func(T) U) Result[U] {
    return Result[U]{val: f(r.val)}
}
Enter fullscreen mode Exit fullscreen mode

The Big Picture

Level Syntax Pattern Idiomatic Use Case
Generic Function func Fn[T constraint](v T) T Standalone utility routines (slices, cmp, algorithms)
Generic Type type Container[T any] struct { v T } Reusable data structures (Box[T], generic queues, caches)
Generic Method (Go 1.27) func (r Result[T]) Transform[U any](f func(T) U) Result[U] Method-level transformations & operations on concrete types

๐Ÿ“Œ Generic Functions

A function that works across multiple types without code duplication.

func PrintAnything[T any](value T) {
    fmt.Println(value)
}

// Usage โ€” Go's type inference handles the call-site
PrintAnything("hello")
PrintAnything(42)
PrintAnything(3.14)
Enter fullscreen mode Exit fullscreen mode

โœ… Tip: Type inference improvements in Go 1.21+ and 1.27 mean you rarely need to explicitly specify type arguments like PrintAnything[string]("hello"). The compiler automatically infers T from the passed arguments.


๐Ÿ“Œ Generic Types

A type (such as a struct) that holds different data types based on how it is declared.

type Box[T any] struct {
    item T
}

func (b Box[T]) Item() T {
    return b.item
}

// Usage
b1 := Box[int]{item: 10}
b2 := Box[string]{item: "hello"}

fmt.Println(b1.Item()) // 10
fmt.Println(b2.Item()) // hello
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Note: The type parameter T is bound to the receiver type Box[T]. Every receiver method on Box[T] automatically gains access to T without needing to redeclare it.


๐Ÿ“Œ Generic Methods (New in Go 1.27)

A method that can declare its own standalone type parameters โ€” even if the receiver type is non-generic or requires a different type parameter. This is the headline addition in Go 1.27.

Example A: Non-Generic Receiver with Generic Method

type Player struct {
    name string
}

// Method declares its own type parameter P
func (p Player) Say[P any](thing P) {
    fmt.Println(p.name, "says:", thing)
}

// Usage
p := Player{name: "Mario"}
p.Say("hello")  // Mario says: hello
p.Say(123)      // Mario says: 123
p.Say(true)     // Mario says: true
Enter fullscreen mode Exit fullscreen mode

Example B: Transforming Types with Transform

Combining generic receiver types with new method type parameters enables smooth functional chaining:

type Result[T any] struct {
    val T
}

// The method declares its own new type parameter U
func (r Result[T]) Transform[U any](f func(T) U) Result[U] {
    return Result[U]{val: f(r.val)}
}

// Usage
res := Result[int]{val: 100}
halved := res.Transform(func(n int) int { return n / 2 })
summary := halved.Transform(func(n int) string {
    return fmt.Sprintf("result=%d", n)
})

fmt.Println(summary.val) // result=50
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” Deep Dive: Seamless Type Inference: Notice how res.Transform(func(n int) string { ... }) doesn't require explicit call-site parameterization like res.Transform[string](...). Go's inference engine inspects the signature of the closure passed into Transform to infer U = string directly.


โš ๏ธ The Gotcha: Generic Methods and Interfaces

Interface methods cannot declare type parameters, and generic methods cannot satisfy interfaces.

type Mapper interface {
    Map[U any](f func(int) U) any
}
// compile error: interface method must have no type parameters
Enter fullscreen mode Exit fullscreen mode

This is a deliberate limit: Go resolves interface satisfaction at compile time, and open-ended generic dispatch would clash with that. If you need interface polymorphism across generic types, put the type parameters on the interface instead:

type Mapper[T, U any] interface {
    Map(f func(T) U) U
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿงช Real-World Example: math/rand/v2

The standard library directly leverages generic methods in Go 1.27. For instance, math/rand/v2 equips (*Rand).N as a generic method operating on integer types, directly matching top-level helper routines:

r := rand.New(rand.NewPCG(1, 2))
fmt.Println(r.N(100)) // int in [0, 100)
Enter fullscreen mode Exit fullscreen mode

Prior to Go 1.27, this call pattern required separate package-level functions. Now, it integrates directly onto the Rand concrete receiver type.


๐Ÿงต Wrapping Up

Go generics now cover the full lifecycle: functions that adapt, types that configure, and methods that extend. Go 1.27's generic methods resolve a long-standing language gap while keeping compilation fast and type safety robust.

๐Ÿ’ฌ How are you planning to use generic methods in your Go 1.27 projects? Are you refactoring package-level utility functions into methods on concrete types?

ย 

๐Ÿ“š Want to go deeper?

ย 
ย 
ย 

This post was reviewed with AI assistance to refine clarity and structure

Top comments (4)

Collapse
 
osvaldo_cunhaneto_df4119 profile image
OSVALDO CUNHA NETO

Whoa, this is fire! ๐Ÿ”ฅ
Another banger from the Idiomatic Go series โ€” and this 1.27 generics cheat sheet is exactly what the dev community needed.

Loved how you broke down the three tiers (functions, types, methods) so clearly, and the hands-on examples with Result.Transform and Player.Say make it super easy to grok. The gotchas section โ€” especially the interface limitation โ€” is a solid heads-up that'll save folks a ton of debugging later.

Also, big props for pulling in math/rand/v2 as a real-world example. Shows this feature is already battle-tested in the stdlib.

Keep dropping this knowledge with your signature clarity and energy. You're leveling up so many gophers out here (myself included)! Can't wait for the next drop in the series. ๐Ÿš€

Mad respect and huge thanks for this gem. ๐Ÿ’™๐Ÿน

Collapse
 
jjpinto profile image
jjpinto

Thanks so much, I really appreciate this! ๐Ÿ™Œ
Generic methods finally make the generics story feel complete, so it was fun to put this cheat sheet together. More idiomatic Go posts on the way! ๐Ÿš€

Collapse
 
vsusini profile image
Vincent

Great read! Go Go!

Collapse
 
jjpinto profile image
jjpinto

Thanks Vincent! Glad you liked it โ€” Go Go! ๐Ÿ˜„