Satisfying a type constraint

August 8, 2023

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

WUT?

I feel like Lone Starr after Dark Helmet says “I am your father’s brother’s nephew’s cousin’s former roommate.”.

Let’s see if we can’t make some sense of this. The spec includes a table of examples that helps a bit:

type argument      type constraint                // constraint satisfaction

int                interface{ ~int }              // satisfied: int implements interface{ ~int }
string             comparable                     // satisfied: string implements comparable (string is stricty comparable)
[]byte             comparable                     // not satisfied: slices are not comparable
any                interface{ comparable; int }   // not satisfied: any does not implement interface{ int }
any                comparable                     // satisfied: any is comparable and implements the basic interface any
struct{f any}      comparable                     // satisfied: struct{f any} is comparable and implements the basic interface any
any                interface{ comparable; m() }   // not satisfied: any does not implement the basic interface interface{ m() }
interface{ m() }   interface{ comparable; m() }   // satisfied: interface{ m() } is comparable and implements the basic interface interface{ m() }

Let’s step back and break down the requirements one at a time.

The first one, I think, is pretty straight forward: “A type T satisfies a constraint C if: T implements C” No problems here.

The seconde one is where I think things get muddy: “C can be written in the form interface{ comparable; E }, where E is a basic interface and T is comparable and implements E.”

Fortunately, this second clause only matters if your type constraint contians the comparable interface, so you can just ignore it in all other cases.

If your constraint does contain the comparable interface, then you have four possible scenarios:

  • It contains only the comparable interface, in which case the first clause is all you need: Does your type implement comparable?
  • It contains the comparable interface and some other type element which is not comparable, introducing a disallowed contradiction, thus it’s not mentioned in the spec.
  • It contains the comparable interface, and some other element type whihc is comparable, making it redundant, thus it’s not mentioned in the spec.
  • It contains the comparable interface, and some other basic interface. This is what’s mentioned in the spec.

I find that breaking it down like this makes it more clear why the spec is worded the way it is. And now it becomes apparent to me that the spec isn’t describing some archane bother’s nephew’s cousin’s roomate type of situation; rather it’s just explaining the only logical combination of comparable and any other constraint.

This section of the spec has one last detail to mention, which I’ll throw in for good measure:

Because of the exception in the constraint satisfaction rule, comparing operands of type parameter type may panic at run-time (even though comparable type parameters are always strictly comparable).

So just avoid doing that. 😊

Or if you can’t, and you need to guard against such runtime panics, you’ll need to use a deferred recover.

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

Share this

Related Content

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.

Type constraints

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:

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.