---
title: "Generic Methods - Angryecho"
description: "Generics was a profound change for Go—it fundamentally expanded the kinds of programs that gophers could write by adding type parameters to the language."
canonical_url: "https://www.angryecho.com/c/Goyu_yan/p/P2TBBk6T3QyhuewS8xhym"
meta:
  "og:description": "Generics was a profound change for Go—it fundamentally expanded the kinds of programs that gophers could write by adding type parameters to the language."
  "og:image": "https://www.angryecho.com/_og/d/c_Post%2Ctitle_~5ZCQ5qe9572R%2Cdescription_~5ZCQ5qe95LiA5LiL77yM5bCx5LiK5ZCQ5qe9572R44CC%2CpostId_P2TBBk6T3QyhuewS8xhym%2Crevision_~MjAyNi0xMC0yOFQwMjo1NjowMC4yNzc5NDRa%2Cp_Ii9jL0dveXVfeWFuL3AvUDJUQkJrNlQzUXlodWV3Uzh4aHltIg%2Cs_9bqRGnMq_E9vf86p.png"
  "og:image:height": "630"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "Generic Methods"
---

> Index of every page on this site: [llms.txt](https://www.angryecho.com/llms.txt)

# **Generic Methods**

Generics was a profound change for Go—it fundamentally expanded the kinds of programs that gophers could write by adding type parameters to the language.

One could now express generic types and functions in Go. These constructs decreased the need for specialized data types and functions, reducing program verbosity and improving the ergonomics of the language in those cases.

To illustrate, different kinds of linked lists could now be condensed into a single type definition:

```
// before Go 1.18
type ListOfInts struct {
elem int
next *ListOfInts
}
type ListOfStrings struct {
elem string
next *ListOfStrings
}
// ...

// after Go 1.18
type List[E any] struct {
elem E
next *List[E]
}
```

Likewise, sorting different kinds of ordered data could be expressed as a single function:

```
// before Go 1.18
func SortInts(s []int) { /* ... */ }
func SortStrings(s []string) { /* ... */ }
// ...

// after Go 1.18
func Sort[E cmp.Ordered](s []E) { /* ... */ }
```

However, no such capability came to methods.

The generics [proposal](https://go.dev/issue/43651) reasoned that because generic interface methods are difficult to implement efficiently (discussed [later](https://go.dev/blog/generic-methods#the-trouble-with-generic-interface-methods)), it didn’t make sense to add type parameters to non-interface (or “concrete”) methods either. In that view, methods are primarily the means of implementing an interface.

Go 1.27 takes a different view and consequently adds generic methods to Go. In this post, we’ll explain this change of view and demonstrate some uses of this new feature.

# Methods for organization

Methods enable organizing functionality around types. To illustrate, let’s reconsider the linked list from above (adding some conveniences):

```
type List[E any] struct {
elem E
next *List[E]
}
func NewList[E any](elems ...E) List[E] { /* ... */ }
func (List[E]) String() string { /* ... */ }
```

Suppose that one wanted to map this structure to hold a value of some other type, like a `string`. A method like `ToString` would do:

```
func (List[E]) ToString(f func(E) string) List[string] { /* ... */ }
```

By providing a “transform” function `f`, `ToString` can be customized with “off-the-shelf” routines.

For example, `strconv.Itoa` might work for a `List[int]`:

```
func main() {
fmt.Println(NewList(1, 2, 3).ToString(strconv.Itoa)) // [1 2 3]
}
```

For a `List[[]byte]`, more options come to mind (depending on the use case):

```
func main() {
l := NewList([]byte("Hallo Welt"), []byte("Helló világ"))
fmt.Println(l.ToString(hex.EncodeToString))                 // [48616c6c6f2057656c74 48656c6cc3b32076696cc3a167]
fmt.Println(l.ToString(base64.StdEncoding.EncodeToString))  // [SGFsbG8gV2VsdA== SGVsbMOzIHZpbMOhZw==]
}
```

Note that parameterizing `List` allowed generalization of the *source* type, but not the *destination* type, since that depends on the transformation being applied. This might be reasonable with just a single destination type, but what if there were many?

In Go 1.18, a generic function could be used for this:

```
// after Go 1.18
func MapList[E, R any](l List[E], f func(E) R) List[R] { /* ... */ }
```

But using a function has the disadvantage of moving `MapList` into the package scope—if many data types support mapping operations, things could get crowded. Furthermore, chained calls must be written “inside out”:

```
func main() {
fmt.Println(MapList(MapList(NewList(0, 2, 4), add(2)), divideBy(2))) // [1 2 3]
}
```

Because Go 1.18 didn’t support generic methods, we needed a generic function as a workaround. This is now remedied in Go 1.27:

```
// after Go 1.27
func (List[E]) Map[R any](f func(E) R) List[R] { /* ... */ }
```

This is compact, expressive, *and* locally scoped. Additionally, chained calls are more readable, as they can be written more naturally from left to right:

```
func main() {
fmt.Println(NewList(0, 2, 4).Map(add(2)).Map(divideBy(2))) // [1 2 3]
}
```

Prefer the “inside out” form? A method expression can convert any method, including a generic one, into its function equivalent. Thus, a function call structure can be recovered if desired:

```
func main() {
f := List[int].Map[int]
fmt.Println(f(f(NewList(0, 2, 4), add(2)), divideBy(2))) // [1 2 3]
}
```

Generic methods, like other generics in Go, must be instantiated (either explicitly or implicitly) before they are used (called or converted to a function).

# Decoupling things

If one views methods *also* as an organizational tool, then Go 1.18’s reasoning appears overly restrictive. While a generic concrete method can’t help implement an interface (without generic interface methods), it can still be useful for code organization. In other words, these concerns can be decoupled.

To elaborate, let’s consider a simple interface:

```
type I interface {
M()
}
type T struct{}                   // a struct, not an interface
func (T) M[P any]() { /* ... */ } // a generic *concrete* method
```

Here, `T.M` is parameterized by `P`. Any instantiation of `T.M` would result in a method signature identical to that of `I.M`:

```
func main() {
T{}.M[int]()
}
```

Both `T.M[int]` and `I.M` have the signature `func M()`. However, this doesn’t mean `T` implements `I`. Interface implementation is a property of a (possibly instantiated) type, not of any particular method. Importantly, `T` does not declare `T.M[int]`, but rather its generic (and thus uninstantiated) counterpart `T.M`.

For `T.M` to participate in interface implementation, there would need to exist a suitable generic method `I.M`:

```
type I interface {
M[P any]() // a generic *interface* method
}
type T struct{}
func (T) M[P any]() { /* ... */ }
```

Since the Go 1.27 syntax doesn’t allow interface methods to declare type parameters, this code is impossible to write.

But *why* can’t Go have generic interface methods? To understand, a bit of a detour is needed.

# The trouble with generic interface methods

An interface value is a box that can hold some other value—the boxed value can be of any type, as long as that type implements the interface. This means that the declared methods on the type are a superset of those declared on the interface.

To illustrate, `T` implements `I` below:

```
type I interface {
M()
}
type T struct{}
func (T) M() { /* ... */ }
```

One can call methods on interface values:

```
func main() {
F(T{})
}

func F(i I) {
i.M() // "backed" by T.M
}
```

Above, it’s easy to observe that the call to `i.M` will be routed to `T.M`. But in more complex programs, the relation between an interface value method call and its *possible* targets is significantly less obvious. This relation typically spans across packages, making it difficult or impossible to deduce in the general case.

To illustrate, let’s introduce a package boundary:

```
// -- package main --
import "p"

func main() {
p.F(T{})
}
type T struct{}
func (T) M() { /* ... */ }

// -- package p --
func F(i I) {
i.M()
}
type I interface {
M()
}
```

Because the two packages are compiled separately, the `main` package won’t know how `p.F` uses `T{}`. To ensure the existence of any method that `p.F` *might* call, the compiler generates code for *all* non-generic methods of a type at its declaration (or instantiation). That way, regardless of what `p.F` does with `T{}`, the necessary code will exist at runtime.

Now, suppose that `I.M` were generic:

```
// -- package main --
import "p"

func main() {
p.F(T{})
}
type T struct{}
func (T) M[P any]() { /* ... */ }

// -- package p --
func F(i I) {
i.M[int]()
}
type I interface {
M[P any]()
}
```

Again, the `main` package won’t know how `p.F` uses `T{}`—including how it might instantiate `T.M`. To handle *any* usage of `T{}` inside `p.F`, the compiler would need to instantiate `T.M` with *every possible type argument*.

This is impractical with Go’s approach to instantiation, wherein the compiler generates specific code for each method based on the type arguments. If method arguments were “boxed” (i.e. passed as values of their constraint interfaces), each instantiation would share the same code. This would avoid having too many instantiations at the cost of indirect call overhead—even for direct calls to instantiated methods.

# Conclusion

Go 1.27 introduces type parameters on concrete methods—this feature has been highly desired by the Go community, as it permits more ergonomic and readable code. Although we can’t support generic interface methods, we decided that allowing type parameters for concrete methods was still worthwhile.

We hope you enjoy using Go’s generic methods and find helpful ways to use them in your projects!

> 原文： [The Go Blog｜Generic Methods](https://go.dev/blog/generic-methods)，2026-08-26

排序方式 :

### **成为第一条评论！**

Nobody's responded to this post yet. Add your thoughts and get the conversation going.

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://www.angryecho.com/c/Goyu_yan/p/P2TBBk6T3QyhuewS8xhym#post","@type":"DiscussionForumPosting","commentCount":0,"dateModified":"2026-10-28T02:56:00.277Z","datePublished":"2026-08-26T04:00:00.000Z","description":"Generics was a profound change for Go—it fundamentally expanded the kinds of programs that gophers could write by adding type parameters to the language.","headline":"Generic Methods","isPartOf":"https://www.angryecho.com/c/Goyu_yan","mainEntityOfPage":"https://www.angryecho.com/c/Goyu_yan/p/P2TBBk6T3QyhuewS8xhym","text":"Generics was a profound change for Go—it fundamentally expanded the kinds of\nprograms that gophers could write by adding type parameters to the language.\n\nOne could now express generic types and functions in Go. These constructs\ndecreased the need for specialized data types and functions, reducing program\nverbosity and improving the ergonomics of the language in those cases.\n\nTo illustrate, different kinds of linked lists could now be condensed into a\nsingle type definition:\n\n// before Go 1.18\ntype ListOfInts struct {\nelem int\nnext *ListOfInts\n}\ntype ListOfStrings struct {\nelem string\nnext *ListOfStrings\n}\n// ...\n\n// after Go 1.18\ntype List[E any] struct {\nelem E\nnext *List[E]\n}\n\nLikewise, sorting different kinds of ordered data could be expressed as a single\nfunction:\n\n// before Go 1.18\nfunc SortInts(s []int) { /* ... */ }\nfunc SortStrings(s []string) { /* ... */ }\n// ...\n\n// after Go 1.18\nfunc Sort[E cmp.Ordered](s []E) { /* ... */ }\n\nHowever, no such capability came to methods.\n\nThe generics proposal reasoned that because generic interface\nmethods are difficult to implement efficiently (discussed\nlater), it didn’t make sense to\nadd type parameters to non-interface (or “concrete”) methods either. In that\nview, methods are primarily the means of implementing an interface.\n\nGo 1.27 takes a different view and consequently adds generic methods to Go. In\nthis post, we’ll explain this change of view and demonstrate some uses of this\nnew feature.\n\nMethods for organization\n\nMethods enable organizing functionality around types. To illustrate, let’s\nreconsider the linked list from above (adding some conveniences):\n\ntype List[E any] struct {\nelem E\nnext *List[E]\n}\nfunc NewList[E any](elems ...E) List[E] { /* ... */ }\nfunc (List[E]) String() string { /* ... */ }\n\nSuppose that one wanted to map this structure to hold a value of some other\ntype, like a string. A method like ToString would do:\n\nfunc (List[E]) ToString(f func(E) string) List[string] { /* ... */ }\n\nBy providing a “transform” function f, ToString can be customized with\n“off-the-shelf” routines.\n\nFor example, strconv.Itoa might work for a List[int]:\n\nfunc main() {\nfmt.Println(NewList(1, 2, 3).ToString(strconv.Itoa)) // [1 2 3]\n}\n\nFor a List[[]byte], more options come to mind (depending on the use case):\n\nfunc main() {\nl := NewList([]byte(\"Hallo Welt\"), []byte(\"Helló világ\"))\nfmt.Println(l.ToString(hex.EncodeToString))                 // [48616c6c6f2057656c74 48656c6cc3b32076696cc3a167]\nfmt.Println(l.ToString(base64.StdEncoding.EncodeToString))  // [SGFsbG8gV2VsdA== SGVsbMOzIHZpbMOhZw==]\n}\n\nNote that parameterizing List allowed generalization of the source type, but\nnot the destination type, since that depends on the transformation being\napplied. This might be reasonable with just a single destination type, but what\nif there were many?\n\nIn Go 1.18, a generic function could be used for this:\n\n// after Go 1.18\nfunc MapList[E, R any](l List[E], f func(E) R) List[R] { /* ... */ }\n\nBut using a function has the disadvantage of moving MapList into the package\nscope—if many data types support mapping operations, things could get crowded.\nFurthermore, chained calls must be written “inside out”:\n\nfunc main() {\nfmt.Println(MapList(MapList(NewList(0, 2, 4), add(2)), divideBy(2))) // [1 2 3]\n}\n\nBecause Go 1.18 didn’t support generic methods, we needed a generic function as\na workaround. This is now remedied in Go 1.27:\n\n// after Go 1.27\nfunc (List[E]) Map[R any](f func(E) R) List[R] { /* ... */ }\n\nThis is compact, expressive, and locally scoped. Additionally, chained calls\nare more readable, as they can be written more naturally from left to right:\n\nfunc main() {\nfmt.Println(NewList(0, 2, 4).Map(add(2)).Map(divideBy(2))) // [1 2 3]\n}\n\nPrefer the “inside out” form? A method expression can convert any method,\nincluding a generic one, into its function equivalent. Thus, a function call\nstructure can be recovered if desired:\n\nfunc main() {\nf := List[int].Map[int]\nfmt.Println(f(f(NewList(0, 2, 4), add(2)), divideBy(2))) // [1 2 3]\n}\n\nGeneric methods, like other generics in Go, must be instantiated (either\nexplicitly or implicitly) before they are used (called or converted to a\nfunction).\n\nDecoupling things\n\nIf one views methods also as an organizational tool, then Go 1.18’s reasoning\nappears overly restrictive. While a generic concrete method can’t help implement\nan interface (without generic interface methods), it can still be useful for\ncode organization. In other words, these concerns can be decoupled.\n\nTo elaborate, let’s consider a simple interface:\n\ntype I interface {\nM()\n}\ntype T struct{}                   // a struct, not an interface\nfunc (T) M[P any]() { /* ... */ } // a generic *concrete* method\n\nHere, T.M is parameterized by P. Any instantiation of T.M would result in\na method signature identical to that of I.M:\n\nfunc main() {\nT{}.M[int]()\n}\n\nBoth T.M[int] and I.M have the signature func M(). However, this doesn’t\nmean T implements I. Interface implementation is a property of a (possibly\ninstantiated) type, not of any particular method. Importantly, T does not\ndeclare T.M[int], but rather its generic (and thus uninstantiated) counterpart\nT.M.\n\nFor T.M to participate in interface implementation, there would need to exist\na suitable generic method I.M:\n\ntype I interface {\nM[P any]() // a generic *interface* method\n}\ntype T struct{}\nfunc (T) M[P any]() { /* ... */ }\n\nSince the Go 1.27 syntax doesn’t allow interface methods to declare type\nparameters, this code is impossible to write.\n\nBut why can’t Go have generic interface methods? To understand, a bit of a\ndetour is needed.\n\nThe trouble with generic interface methods\n\nAn interface value is a box that can hold some other value—the boxed value can\nbe of any type, as long as that type implements the interface. This means that\nthe declared methods on the type are a superset of those declared on the\ninterface.\n\nTo illustrate, T implements I below:\n\ntype I interface {\nM()\n}\ntype T struct{}\nfunc (T) M() { /* ... */ }\n\nOne can call methods on interface values:\n\nfunc main() {\nF(T{})\n}\n\nfunc F(i I) {\ni.M() // \"backed\" by T.M\n}\n\nAbove, it’s easy to observe that the call to i.M will be routed to T.M. But\nin more complex programs, the relation between an interface value method call\nand its possible targets is significantly less obvious. This relation\ntypically spans across packages, making it difficult or impossible to deduce in\nthe general case.\n\nTo illustrate, let’s introduce a package boundary:\n\n// -- package main --\nimport \"p\"\n\nfunc main() {\np.F(T{})\n}\ntype T struct{}\nfunc (T) M() { /* ... */ }\n\n// -- package p --\nfunc F(i I) {\ni.M()\n}\ntype I interface {\nM()\n}\n\nBecause the two packages are compiled separately, the main package won’t know\nhow p.F uses T{}. To ensure the existence of any method that p.F might\ncall, the compiler generates code for all non-generic methods of a type at its\ndeclaration (or instantiation). That way, regardless of what p.F does with\nT{}, the necessary code will exist at runtime.\n\nNow, suppose that I.M were generic:\n\n// -- package main --\nimport \"p\"\n\nfunc main() {\np.F(T{})\n}\ntype T struct{}\nfunc (T) M[P any]() { /* ... */ }\n\n// -- package p --\nfunc F(i I) {\ni.M[int]()\n}\ntype I interface {\nM[P any]()\n}\n\nAgain, the main package won’t know how p.F uses T{}—including how it might\ninstantiate T.M. To handle any usage of T{} inside p.F, the compiler\nwould need to instantiate T.M with every possible type argument.\n\nThis is impractical with Go’s approach to instantiation, wherein the compiler\ngenerates specific code for each method based on the type arguments. If method\narguments were “boxed” (i.e. passed as values of their constraint interfaces),\neach instantiation would share the same code. This would avoid having too many\ninstantiations at the cost of indirect call overhead—even for direct calls to\ninstantiated methods.\n\nConclusion\n\nGo 1.27 introduces type parameters on concrete methods—this feature has been\nhighly desired by the Go community, as it permits more ergonomic and readable\ncode. Although we can’t support generic interface methods, we decided that\nallowing type parameters for concrete methods was still worthwhile.\n\nWe hope you enjoy using Go’s generic methods and find helpful ways to use them\nin your projects!\n\n\n\n原文：The Go Blog｜Generic Methods，2026-08-26","url":"https://www.angryecho.com/c/Goyu_yan/p/P2TBBk6T3QyhuewS8xhym","author":{"@type":"Person","name":"hepingfly","url":"https://www.angryecho.com/u/P7H8281102"},"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/DislikeAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":0}]}]}
```

## Sitemap

See the full [sitemap](https://www.angryecho.com/sitemap.md) for all pages.
