Skip to content

proposal: spec: support user-defined variance (allow type parameters as type bounds) #67513

Closed as not planned
@Garciat

Description

@Garciat

Go Programming Experience

Experienced

Other Languages Experience

Java, Haskell, Python, C++

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?

Sort of: #47127

Also related: #58590

Does this affect error handling?

No.

Is this about generics?

It relates to type constraints/bounds.

Proposal

Allow type parameters as type bounds:

func cast[T U, U any](t T) U { return t }

Note that this not include things like:

  • [T ~U, U any]
  • [T U | int, U any]
  • [T interface{ U }, U any] -- maybe we do want to allow this? But syntactically, it opens the floodgates
  • [T interface{ U; V }, U any, V any]
  • or any other type expression that is not a single type parameter reference

Language Spec Changes

I don't know exactly how, but some clause in https://go.dev/ref/spec#TypeConstraint would need to be updated.

If [T interface{ U }, U any] is allowed, then https://go.dev/ref/spec#General_interfaces would need to be updated as well.

Informal Change

No response

Is this change backward compatible?

Yes.

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

No response

Performance Costs

No response

Prototype

I prototyped the change in Garciat@c69063f.

Type-checking works as expected:

func cycle[A B, B C, C D, D A]() {} // new error: invalid recursive type parameter constraint

func term[T interface{ U }, U any]() {} // error: term cannot be a type parameter

func cast[T U, U any](t T) U { return t } // OK

type X struct{}
func (_ X) M() {}

func main() {
  cast[string, string]("hi")           // error: string does not satisfy string (string is not an interface)
  cast[X, interface{}](X{})            // OK
  cast[X, interface{ M() }](X{})       // OK
  cast[X, interface { M(); N() }](X{}) // error: X does not satisfy interface{M(); N()} (missing method N)
}

Compilation also seems to be doing the right thing; the code runs as expected.

However, getting full type parameter embedding in interfaces ([T interface{ U }, U any]) seems to be a much more involved code change.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions