Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: spec: Enhance Generics to Support Pointer Receivers in Go #70960

Closed
2 of 4 tasks
FwP-IDN opened this issue Dec 22, 2024 · 7 comments
Closed
2 of 4 tasks

proposal: spec: Enhance Generics to Support Pointer Receivers in Go #70960

FwP-IDN opened this issue Dec 22, 2024 · 7 comments
Labels
LanguageChange Suggested changes to the Go language LanguageChangeReview Discussed by language change review committee Proposal
Milestone

Comments

@FwP-IDN
Copy link

FwP-IDN commented Dec 22, 2024

Go Programming Experience

Intermediate

Other Languages Experience

Go, Python, Rust, Java, JS

Related Idea

  • Has this idea, or one like it, been proposed before?
  • Does this affect error handling?
  • Is this about generics?
  • Is this change backward compatible? Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit

Has this idea, or one like it, been proposed before?

Yes

Does this affect error handling?

No

Is this about generics?

Yes, this proposal is about generics. While it builds on Go's accepted generics design, it addresses a specific gap that has not been tackled in previous proposals: The inability to constrain type parameters to pointers and handle pointer receiver methods in generic functions.

I found no similar proposal, making this a unique addition to Go’s generics. It introduces pointer-specific constraints and safe instantiation, enabling type-safe patterns like cloning while keeping Go simple and secure, eliminating the need for unsafe type conversions or extra checks to ensure correctness..

Proposal

Proposed Change
This proposal introduces pointer-specific constraints for generics in Go, allowing type parameters to be constrained to pointer types (*T). This enables developers to more safely and effectively work with structs that have pointer receivers in generic functions, addressing limitations that arise when trying to use pointer receivers in generics.

Who Does This Proposal Help, and Why?
This proposal helps Go developers working with generics, especially when dealing with structs that have pointer receivers. It eliminates the need for complex type conversions and additional runtime checks, enabling more intuitive and type-safe code while keeping Go simple.

Change to the Language
Pointer-Specific Constraints: The core change is the introduction of pointer-specific constraints for generics. By allowing type parameters to be constrained to pointer types (*T), this proposal makes it easier to work with structs that have pointer receivers in generic functions.

Language Spec Changes

Change to the Language Specification

This proposal adds support for pointer-specific constraints in Go’s generics. The main change is the ability to limit type parameters to pointer types (*T), allowing generic functions to work with pointers in a safe and straightforward way.

Key Changes:

Pointer-Specific Constraints:

The language will allow type parameters in generics to be constrained to pointer types, such as *T. This enables functions and types to accept only pointers to certain types.

For example, a function could be defined like this:

func Clone[*T *Object](o T) *T

Current Problems Without Pointer Constraints:

Problem Statement Example: Cloning an Object with Pointer Receiver

We have an interface Object and a struct ObjectA implementing it. The struct ObjectA uses a pointer receiver for its method. The goal is to create a generic cloning function that can directly handle this scenario without requiring a type conversion from the interface to the struct.

  • Problem 1: If we try to use a type parameter with an interface constraint and pass a pointer to it (e.g., [T InterfaceConstraint](obj *T) *T), it gets rejected by the compiler because the method is expected to use a pointer receiver, but Go cannot safely handle this in the current generic system.
type Object interface {
    GetName() string
}

type ObjectA struct {
    Name string
}

func (a *ObjectA) GetName() string {
    return a.Name
}

// Problem 1: This is rejected by the compiler because `T` isn't constrained to pointer types.
func Clone[T Object](obj *T) *T {
    return obj
}
  • Problem 2: If we try to use a type parameter with a value ([T InterfaceConstraint](obj T) T), Go will treat T as a value, not a pointer. As a result, Go replaces T with the value type (e.g., *SomeStruct), but we cannot use new(SomeStruct) or new(T) to instantiate the pointer because T is treated as a value type, not a pointer.
type Object interface {
    GetName() string
}

type ObjectA struct {
    Name string
}

`T` is treated as a value type, and `new(T)` won't work as `T` is not constrained to pointer types.
func Clone[T Object](obj T) T {
    return obj
}

With Pointer-Specific Constraints (After Proposal):

type Object interface {
    GetName() string
}

type ObjectA struct {
    Name string
}

func (a *ObjectA) GetName() string {
    return a.Name
}

// This works because *T (pointer) is used to check against the Object interface, not the struct itself.
func Clone[*T Object](obj *T) *T {
    return obj
}

Informal Change

No response

Is this change backward compatible?

Yes, this change is fully backward compatible. It introduces a new feature that allows pointer receivers to be used in generic constraints, but it does not modify or invalidate any existing syntax. All previously valid code remains functional.

Orthogonality: How does this change interact or overlap with existing features?

No response

Would this change make Go easier or harder to learn, and why?

No response

Cost Description

No response

Changes to Go ToolChain

go compiler

Performance Costs

What is the compile-time cost? The compile-time cost is O(1). It involves passing a single boolean flag (checkWithPointerInstead) and applying a simple logic to treat the struct type as a pointer during type checking. This addition does not introduce significant overhead to the compilation process. What is the runtime cost? There is no change in runtime cost. The proposed feature is resolved entirely at compile time, meaning the resulting binary executes with the same efficiency as before, without any additional runtime overhead.

Prototype

the PR draft is here: #70959

and the PR works on my local

image

The change is primarily focused on the syntax parser and type checker.

In the syntax parser, when the type *T is encountered with an interface like SomeInterface, a flag checkWithPointerInstead is set to true and passed to the type checker.

During the type checking phase, if the checkWithPointerInstead flag is true, the type is transformed into a pointer type. This is done by wrapping the original type with a Pointer structure like this:
V = &Pointer{base: V}.

This transformation allows for proper handling of pointer types during type checking without altering the existing syntax or runtime behavior.

@FwP-IDN FwP-IDN added LanguageChange Suggested changes to the Go language LanguageChangeReview Discussed by language change review committee Proposal labels Dec 22, 2024
@gopherbot gopherbot added this to the Proposal milestone Dec 22, 2024
@seankhliao
Copy link
Member

I believe the appropriate constraint is

type Object[T any] interface {
    GetName() string
    *T
}

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Dec 22, 2024
@FwP-IDN FwP-IDN changed the title proposal: spec: proposal title proposal: spec: Enhance Generics to Support Pointer Receivers in Go Dec 22, 2024
@ifteneiftene
Copy link

I believe the appropriate constraint is

type Object[T any] interface {
    GetName() string
    *T
}

Oui

@FwP-IDN
Copy link
Author

FwP-IDN commented Dec 22, 2024

@seankhliao

Screenshot 2024-12-22 at 20 33 01

That's not solve the problem, cannot initialize *O using new / &O{}

@FwP-IDN
Copy link
Author

FwP-IDN commented Dec 22, 2024

Screenshot 2024-12-22 at 20 35 07

if using *O it's rejected by the compiler

@seankhliao
Copy link
Member

seankhliao commented Dec 22, 2024

var o O = new(T)

@FwP-IDN
Copy link
Author

FwP-IDN commented Dec 22, 2024

I see. Thanks for the information 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LanguageChange Suggested changes to the Go language LanguageChangeReview Discussed by language change review committee Proposal
Projects
None yet
Development

No branches or pull requests

5 participants