Type constraints

August 4, 2023

Are you as curious about Fuzzing in Go as I am? On Monday’s live stream, I’ll be live coding my way through John Arundel’s 4-part series on Go Fuzzing. I hope you’ll [join me!](https://youtube.com/live/VKV-sFFeSQw


Type constraints

A type constraint is an interface that defines the set of permissible type arguments for the respective type parameter and controls the operations supported by values of that type parameter.

TypeConstraint = TypeElem .

If the constraint is an interface literal of the form interface{E} where E is an embedded type element (not a method), in a type parameter list the enclosing interface{ … } may be omitted for convenience:

[T []P]                      // = [T interface{[]P}]
[T ~int]                     // = [T interface{~int}]
[T int|string]               // = [T interface{int|string}]
type Constraint ~int         // illegal: ~int is not in a type parameter list

We’ve already seen type constraints, so they shouldn’t be a surprise. But here we get a nice juicy tidbit: The interface{ … } syntax is optional in many cases. Actually, what’s likely more surprising is not that it can be omitted, since that’s how we usually see it, but that it can be included.

By way of example, the following type constraints are equivalent:

func print1[T int | string](arg T) {
	fmt.Print(arg)
}

func print2[T interface{ int | string }](arg T) {
	fmt.Print(arg)
}

See it in the playground.

But this only works for type elements, not methods.

func print3[T String() string](arg T) {} // Invalid!

func print4[T interface{ String() string}](arg T) {} // Valid

Quotes from The Go Programming Language Specification Version of December 15, 2022

Share this

Related Content

Satisfying a type constraint

Satisfying a type constraint A type argument T satisfies a type constraint C if T is an element of the type set defined by C; i.e., if T implements C. As an exception, a strictly comparable type constraint may also be satisfied by a comparable (not necessarily strictly comparable) type argument. More precisely: A type T satisfies a constraint C if T implements C; or C can be written in the form interface{ comparable; E }, where E is a basic interface and T is comparable and implements E.

Type parameters in method definitions

Yesterday we saw that when a method is defined on a generic type, the receiver must include type parameters. Now for all the relevant details: Method declarations … … Syntactically, this type parameter declaration looks like an instantiation of the receiver base type: the type arguments must be identifiers denoting the type parameters being declared, one for each type parameter of the receiver base type. The type parameter names do not need to match their corresponding parameter names in the receiver base type definition, and all non-blank parameter names must be unique in the receiver parameter section and the method signature.

The comparable interface

Are you as curious about Fuzzing in Go as I am? On today’s live stream, I’ll be live coding my way through John Arundel’s 4-part series on Go Fuzzing. I hope you’ll join me! Today we’re learning about the predeclared comparable interface. But before this discussion makes sense, we should jump ahead to one key definition. That of the concept of “strictly comparable”: A type is strictly comparable if it is comparable and not an interface type nor composed of interface types.