Type parameter restrictions

May 2, 2023

We’ll get to a full discussion of type parameters later on, but knowing at least what they look like is useful for the next section. So here’s an example, used in the definition of a generic function:

func min[T ~int|~float64](x, y T) T {

In this example, min[T ~int|~float64] represents the type parameter.

With that in mind…

General interfaces

The type T in a term of the form T or ~T cannot be a type parameter, and the type sets of all non-interface terms must be pairwise disjoint (the pairwise intersection of the type sets must be empty). Given a type parameter P:

interface {
	P                // illegal: P is a type parameter
	int | ~P         // illegal: P is a type parameter
	~int | MyInt     // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
	float32 | Float  // overlapping type sets but Float is an interface
}

This means, for example, that within our above min function, the following would be illegal:

func min[T ~int|~float64](x, y T) T {
	var x interface {
		T // illegal because T is a type parameter of the generic function
	}

We also can’t combine types and underlying types that overlap. At least this restriction doesn’t require any special understanding of generics that we haven’t covered yet.

type MyInt int
interface {
	~int | MyInt // Illegal becasue of overlapping type sets
}

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

Share this

Related Content

Interface type sets

With what we’ve learned so far about interfaces, we can now draw some general conclusions about the precise definitions of an interfce’s type set. The spec summarizes nicely. General interfaces … Together with method specifications, these elements enable the precise definition of an interface’s type set as follows: The type set of the empty interface is the set of all non-interface types. The type set of a non-empty interface is the intersection of the type sets of its interface elements.

General interfaces

Today we’re venturing into generics territory. General interfaces In their most general form, an interface element may also be an arbitrary type term T, or a term of the form ~T specifying the underlying type T, or a union of terms t1|t<sub<>2|…|tn. We haven’t actually learned about type terms yet–they’re coming in a few weeks. But for now, the TL;DR is that they let us specify a type by its name, rather than by its method(s).

Unions of type sets

General interfaces … Union elements denote unions of type sets: // The Float interface represents all floating-point types // (including any named types whose underlying types are // either float32 or float64). type Float interface { ~float32 | ~float64 } Here we see our first example of a union of type sets. The example shows an interface of all types with an underlying float type. The equivalent for integer types is a bit longer, due to the large number of distinct integer types in Go, but would look like: