spec: generic methods for Go · Issue #77273 · golang/go · GitHub
Skip to content
Navigation Menu
Toggle navigation
Sign in
Appearance settings
PlatformAI CODE CREATIONGitHub CopilotWrite better code with AIGitHub SparkBuild and deploy intelligent appsGitHub ModelsManage and compare promptsMCP RegistryNewIntegrate external toolsDEVELOPER WORKFLOWSActionsAutomate any workflowCodespacesInstant dev environmentsIssuesPlan and track workCode ReviewManage code changesAPPLICATION SECURITYGitHub Advanced SecurityFind and fix vulnerabilitiesCode securitySecure your code as you buildSecret protectionStop leaks before they startEXPLOREWhy GitHubDocumentationBlogChangelogMarketplaceView all featuresSolutionsBY COMPANY SIZEEnterprisesSmall and medium teamsStartupsNonprofitsBY USE CASEApp ModernizationDevSecOpsDevOpsCI/CDView all use casesBY INDUSTRYHealthcareFinancial servicesManufacturingGovernmentView all industriesView all solutionsResourcesEXPLORE BY TOPICAISoftware DevelopmentDevOpsSecurityView all topicsEXPLORE BY TYPECustomer storiesEvents & webinarsEbooks & reportsBusiness insightsGitHub SkillsSUPPORT & SERVICESDocumentationCustomer supportCommunity forumTrust centerPartnersView all resourcesOpen SourceCOMMUNITYGitHub SponsorsFund open source developersPROGRAMSSecurity LabMaintainer CommunityAcceleratorGitHub StarsArchive ProgramREPOSITORIESTopicsTrendingCollectionsEnterpriseENTERPRISE SOLUTIONSEnterprise platformAI-powered developer platformAVAILABLE ADD-ONSGitHub Advanced SecurityEnterprise-grade security featuresCopilot for BusinessEnterprise-grade AI featuresPremium SupportEnterprise-grade 24/7 supportPricing
Search or jump to...
Search code, repositories, users, issues, pull requests...
Search
Clear
Search syntax tips
Provide feedback
We read every piece of feedback, and take your input very seriously.
Include my email address so I can be contacted
Cancel
Submit feedback
Saved searches Use saved searches to filter your results more quickly
Name
Query
To see all available qualifiers, see our documentation.
Cancel
Create saved search
Sign in
Sign up
Appearance settings
Resetting focus
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
Dismiss alert
golang /
go
Public
Notifications You must be signed in to change notification settings
Fork 19.1k
Star 134k
Code
Issues 5k+
Pull requests 472
Discussions
Actions
Projects
Wiki
Security and quality 0
Insights
Additional navigation options
Code
Issues
Pull requests
Discussions
Actions
Projects
Wiki
Security and quality
Insights
spec: generic methods for Go #77273New issueCopy linkNew issueCopy linkClosed0 / 10 of 1 issue completedClosedspec: generic methods for Go#772730 / 10 of 1 issue completedCopy linkLabelsLanguageChangeSuggested changes to the Go languageSuggested changes to the Go languageProposalProposal-AcceptedgenericsIssue is related to genericsIssue is related to genericsrelease-blockerMilestoneGo1.27Descriptiongriesemeropened on Jan 22, 2026Issue body actionsProposal: Generic Methods for Go A change of view. Background For clarity, in the following we use the term concrete method (or just method when the context is clear) to describe a non-interface method declared like a function but with a receiver; and we use the term interface method to describe the name and signature of a method of an interface. Per the current spec, a concrete method is a function with a receiver. Syntactically this is not quite true: while functions can be generic, methods cannot. They cannot declare new type parameters themselves; but they can have a receiver of a generic type (and thus have method-local names for the type parameters of the receiver type). A reason for this discrepancy is that we have historically viewed the primary role of methods as a means to implement an interface: permitting type parameters on concrete methods would imply that we must also permit type parameters on interface methods. Go doesn't support such generic interface methods because we don't know how to implement (calls of) them, or at least we don't know how to implement them efficiently. Specifically, because Go doesn't require a concrete type to declare the interfaces it implements, and instead this is a dynamic property, it cannot be known at compile time which of the infinite possible instantiations of concrete methods will be needed at run time. This shortcoming has long been known and was discussed at length in the original Type Parameters Proposal. But concrete methods are not just a means for implementing interfaces. A method is a function associated with a type, and accessed through the namespace of that type. Therefore methods are useful for organizing code even if they don't ever implement an interface. Furthermore there is a syntactic benefit: x.a().b().c() may naturally be read left to right, whereas c(b(a(x))) is evaluated inside out. Both these aspects of methods are also well known. For these reasons, Go users have requested generic methods for a long time and the idea is widely popular. There are at least two proposals filed on the issue tracker:
#49085 (Allow type parameters in methods, Oct. 2021, with > 900 positive emojis) #50981 (Add generics to methods, Feb. 2022)
So far we have resisted adding generic concrete methods because it always implied that we also needed generic interface methods. The Go FAQ even states that "we do not anticipate that Go will ever add generic methods". Perhaps a change of view is in order: concrete methods are a language feature that is useful in itself, irrespective of interfaces. If a concrete method is a function with a receiver, a generic concrete method can be a generic function with a receiver. The fact that such methods may not be invoked via an interface is an orthogonal aspect: if an interface syntactically can’t include a method with type parameters, then a generic concrete method naturally plays no role in satisfying that interface because there can’t be an interface method with matching type parameters. Note that the original Type Parameters Proposal discussed this alternative view as well, as expressed in the following paragraph: "Or, we could decide that parameterized methods do not, in fact, implement interfaces, but then it's much less clear why we need methods at all. If we disregard interfaces, any parameterized method can be implemented as a parameterized function." We may have reached some clarity on this point: generic concrete methods are useful by themselves, even if they don't implement interface methods. Proposal We propose that concrete method declarations should look exactly like function declarations, but with receivers. Specifically, the syntax of a method declaration should accept type parameters as do function declarations. The syntax of function declarations is as follows: FunctionDecl = "func" FunctionName [ TypeParameters ] Signature [ FunctionBody ] . The syntax of method declarations should be changed from (old): MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] . to (new): MethodDecl = "func" Receiver MethodName [ TypeParameters ] Signature [ FunctionBody ] . Alternatively, to the same effect, the syntax can express directly what we say in prose: a method (declaration) is a function (declaration) with a receiver, and combine the two productions. This removes a syntax production and emphasizes the similarities: FunctionDecl = "func" [ Receiver ] identifier [ TypeParameters ] Signature [ FunctionBody ] . The scope of an identifier denoting a type parameter of a generic method begins after the name of the method and ends at the end of the method body. This matches existing rules for type parameters of functions and method receivers. Constraints for method type parameters may refer to type parameters declared with the method receiver because they are in scope. Calling a generic concrete method works as expected: either, type arguments are provided as needed, or type inference determines any missing type arguments, exactly like for generic function calls. Method expressions and method values continue to work as expected: if the method is generic, the resulting function is generic and the existing rules for using generic functions apply. The grammar will need a small adjustment: currently type arguments may only be supplied to operand names which are (possibly package-qualified) identifiers for generic functions and types. Per the syntax for operands (old): Operand = Literal | OperandName [ TypeArgs ] | "(" Expression ")" . … OperandName = identifier | QualifiedIdent . TypeArgs = "[" TypeList [ "," ] "]" . Methods can be attached to arbitrary user-defined types, and values of such types may be denoted by various expressions, not just qualified identifiers. To permit instantiation of generic methods, type arguments must move from the syntax for operands to the syntax for primary expressions, resulting in the updated productions (new): Operand = Literal | OperandName | "(" Expression ")" .
PrimaryExpr = Operand | Conversion | MethodExpr | PrimaryExpr Selector | PrimaryExpr Index | PrimaryExpr Slice | PrimaryExpr TypeAssertion | PrimaryExpr Arguments | PrimaryExpr TypeArgs . (Notably, type arguments are already parsed as part of primary expressions in the current implementation. This is necessary because an instantiation such as T[int] and an index expression a[i] are syntactically indistinguishable. In the language specification, the syntactic overlap between these two is maintained solely for the sake of documentation.) Methods of interfaces are not changed. Importantly, a generic concrete method does not match against an interface method with the same name and signature because the interface method syntactically cannot have matching type parameters. Generic methods also won't be accessible via reflection (by name or index) for the same reason that uninstantiated generic functions are not accessible: the reflect package doesn't have a mechanism to instantiate a generic value or type (and it is unclear how one would provide such a mechanism). This is the entirety of the proposal. Examples Given a (non-interface) receiver base type S, we may associate a generic method m with it: type S struct { … } func (*S) m[P any](x P) { … } Given a variable s of type S (or a value of type *S), we can call m as follows: var s S s.m[int](42) // explicit type argument int s.m(x) // type argument P is inferred from x A receiver base type may itself be generic: type G[P any] struct{ … } func (*G[P]) m[Q any](x Q) { … } Calling m requires a variable whose type is an instantiation of G, but there is no other difference in the way m is called. An interface I with a method m: type I interface { m(string) } may be implemented by a suitably instantiated generic type G (this is true in current Go): type G[P any] struct{ … } func (G[P]) m(P) { … }
var g G[string] var _ I = g // valid because G[string].m has signature m(string) which matches m(string) of I But I is not implemented by a type H with a generic method m: type H struct{ … } func (H) m[P any](P) { … }
var h H var _ I = h // invalid because H.m has signature m[P any](P) which doesn't match m(string) of I Here's another, less abstract example, related to the Go library. A Reader type with a generic Read method: type Reader struct{ … } func (*Reader) Read[E any]([]E) (int, error) { … } does not implement io.Reader, even though it might if there were some way to instantiate the method as (*Reader).Read[byte] (which there is not, and we are not proposing it). Method expressions (and method values) work as expected. Given the Reader type from above, the method expression Reader.Read produces a generic function with type parameters and signature: [E any](*Reader, []E) (int, error) If the type itself is generic, it must be instantiated (per existing Go rules for generic types) before it can be used in a method expression. For instance, given a generic List type with a generic formatting method format: type List[E any] struct { … } func (*List[E]) format[F any](E, F) string { … } the method expression List[string].format will result in a function with type parameters and signature [F any](string, F) string In particular, List.format is not a valid method expression (List is not instantiated), nor does it produce the generic function signature [E any, F any](E, F) string // not the signature of List.format Implementation Specification The change to the language specification is small and fully backward-compatible. The syntax changes have been outlined above. The corresponding prose changes are similarly straightforward. Whenever there is a language change, updating the specification is a tiny part of the work needed to get documentation updated in general. This includes existing documents traditionally maintained by the Go team (which we plan to keep fresh as much as possible), to 3P documentation beyond our control (external blog post, articles, books, etc.). These will become partly out of date and may or may not be updated, but that is not avoidable if we want to make changes to the language. Compiler The parser already accepts type parameters for methods for robustness but complains with an error. Removing that error is a trivial change. The parser also already handles index expressions and instantiations together. If any changes are needed at all, they will be minimal. The type checker will need various adjustments, most likely involving the removal of restrictions, the details of which will only become apparent by doing the actual work. The compiler back-end changes are likely more significant. That said, method calls via non-interface receivers can be resolved statically (at compile time): because the type of the receiver is not an interface, its type is statically known, and thus the called method is also statically known. Conceptually, such method calls can be rewritten into function calls. If the method itself is generic, the method call can be rewritten into a call of a corresponding generic function. For instance, given the type G, method m, and instance x of G: type G[P any] struct{ … } func (G[P]) m[Q any](x Q) { … }
var g G[string] the method call var s string g.m(s) can be translated into a function call f(s) of the generic function func f[Q any](g G[string], x Q) { G[string].m[Q](g, x) // using a call of method expression G[string].m } or func f[Q any](g G[string], x Q) { g.m[Q](x) // using a call of method value g.m[Q] } This is not to say that an implementation will be trivial. But the principal translation mechanism is understood. The import/export data format will need to be updated to allow for type parameters on methods. This is likely the most disruptive change because of the many different exporters and importers that exist, some of which are used for language tools, reside in different repositories, and all must remain in sync with the compiler's export format. Carefully staged updates are needed in order to not break existing infrastructure. We have done import/export data format changes in the past, and while tedious, they can be done. Libraries and tools No change of the existing libraries is required, but future (versions of these) libraries may want to use generic methods. The impact on tools may be significant as they will need to handle the new language feature. The go/types API's Signature type already provides accessors for receiver and ordinary type parameters, so it's possible that no API changes are needed there, but go/types clients cannot rely on the (current) fact that only one of type parameter lists is present. Judging from past experience, it may take one or two release cycles for all tools to catch up. It is difficult to judge the amount work involved without actually doing the work. Other considerations This proposal adds a new feature to the language by removing a restriction; thus it is fully backward-compatible with existing Go. Importantly, it also doesn't preclude the implementation of generic interface methods at some point, should we find an acceptable implementation solution that doesn't impose a cost when the feature is not used. Generic functions and types make it possible to write more complex code and adding generic methods will further increase that capability. But used appropriately, generic methods can simplify and generalize situations where currently more complex work-arounds are needed (see #49085 for an example).Reactions are currently unavailableMetadataMetadataAssigneesNo one assignedLabelsLanguageChangeSuggested changes to the Go languageSuggested changes to the Go languageProposalProposal-AcceptedgenericsIssue is related to genericsIssue is related to genericsrelease-blockerTypeNo typeFieldsGive feedbackNo fields configured for issues without a type.ProjectsProposalsStatusAcceptedShow more project fieldsMilestoneGo1.27No due dateRelationshipsNone yetDevelopmentNo branches or pull requestsIssue actions
Footer
© 2026 GitHub, Inc.
Footer navigation
Terms
Privacy
Security
Status
Community
Docs
Contact
Manage cookies
Do not share my personal information
You can’t perform that action at this time. |
The proposal for generic methods in Go addresses a long-standing desire among users for more flexible code organization, moving beyond the historical viewpoint that methods are solely means for implementing interfaces. The background explains that concrete methods, defined as functions with receivers, offer organizational and syntactic benefits, such as allowing natural left-to-right method chaining. The core conflict stems from the idea that introducing type parameters to concrete methods would necessitate the allowance of generic interface methods, which the authors noted Go historically avoids due to implementation complexities. The proposal argues for a change in perspective, asserting that generic concrete methods possess utility on their own, irrespective of their relationship with interfaces.
The proposed syntactic change involves modifying method declarations to allow for type parameters, mirroring function declarations. Specifically, the syntax for a method declaration should permit type parameters, altering the structure from a function declaration with a receiver to incorporate generic type parameters directly into the method definition. This aims to allow methods to operate on parameterized types. The proposal details how this change impacts expression evaluation; generic concrete method calls should behave predictably, either by explicitly providing type arguments or through type inference, similar to calls to generic functions.
The proposal addresses several technical consequences of this change. While generic concrete methods do not automatically satisfy interface methods because interfaces cannot syntactically contain methods with type parameters, this structural separation is seen as valid. The text notes that this distinction is orthogonal: the existence of a generic concrete method does not imply the existence of a generic interface method. The authors suggest that if a generic concrete method does not implement an interface method, it naturally plays no role in interface satisfaction.
The proposed changes require adjustments across the language specification, compiler, and data interchange formats. The compiler back-end changes are anticipated to be significant, requiring careful work to ensure static resolution mechanisms still function correctly. Since method calls by non-interface receivers are statically resolvable based on the receiver's concrete type, generic methods can conceptually be translated into calls to corresponding generic functions. This translation mechanism is outlined as a principal way the compiler can handle these calls. Furthermore, updating the import/export data format is identified as a potentially disruptive but necessary change, as this format must synchronize with the compiler's export mechanisms to accommodate method type parameters. The modifications aim to enhance the language's capability to write more complex and generalized code without sacrificing backward compatibility with existing Go features. |