Satisfying a type constraint
August 8, 2023
Satisfying a type constraint
A type argument
T
satisfies a type constraintC
ifT
is an element of the type set defined byC
; i.e., ifT
implementsC
. 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 constraintC
if
T
implementsC
; orC
can be written in the forminterface{ comparable; E }
, whereE
is a basic interface andT
is comparable and implements E.
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 implementcomparable
? - It contains the
comparable
interface and some other type element which is notcomparable
, introducing a disallowed contradiction, thus it’s not mentioned in the spec. - It contains the
comparable
interface, and some other element type whihc iscomparable
, 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