Go 1.18. Type Constraints are (not) interfaces!?

697 views
Skip to first unread message

Leonard Mittmann

unread,
Dec 15, 2021, 6:23:48 AM12/15/21
to golang-nuts
I just learned that type constraints, which are defined as interfaces are actually not usable in all the places "normal" interfaces can be used in. E.g., why can a constraint interface not be uses as a struct type?

Let's say I have the func `func Smallest[T constraints.Ordered](s []T) T`. How do I write a table test for this func? Intuitively one would try to store tests like this:

tests := []struct {
    Arr: []constraints.Ordered
    Want: constraints.Ordered
}{...}

But this is not permitted. Am I missing something why this behavior is should be considered good? If a type constraint is nothing like an interface... okay, but why call it interface then?

Kurtis Rader

unread,
Dec 15, 2021, 10:47:20 AM12/15/21
to Leonard Mittmann, golang-nuts
This was asked 15 hours ago in the thread with the subject line "Go 1.18 beta1: Embedding Type Parameter in struct definition is an error". :-)

See https://golang.org/issue/49030.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/53df110e-8f34-490a-9b77-610dacffdbcfn%40googlegroups.com.


--
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank

Jason Phillips

unread,
Dec 15, 2021, 11:05:36 AM12/15/21
to golang-nuts
@Leonard, type constraints can only be used as type parameters, using them as normal interfaces is currently not allowed. See the notes in the draft release notes[1] or the draft 1.18 spec[2].

> Such interfaces may only be used as type constraints.

> Interfaces that contain non-interface types, terms of the form ~T, or unions may only be used as type constraints, or as elements of other interfaces used as constraints. They cannot be the types of values or variables, or components of other, non-interface types.

@Kurtis, that issue is about embedded fields (fields without explicit field names) that use type parameters.

Jason Phillips

unread,
Dec 15, 2021, 11:08:51 AM12/15/21
to golang-nuts

Brian Hatfield

unread,
Dec 15, 2021, 11:29:55 AM12/15/21
to Jason Phillips, golang-nuts
I read that and I genuinely do not understand why interfaces now mean two distinct incompatible things. I think this is going to confuse a lot of people.

Brian Candler

unread,
Dec 15, 2021, 11:48:33 AM12/15/21
to golang-nuts
A function which takes a value of an interface type, and a function which takes a value of a constrained generic type, are certainly two different things.

In the first case, it gets a dynamic boxed value which at runtime may contain a value of any type that implements that interface - or no value at all (nil interface value).

In the second case, it gets a static value of a concrete type, where the type is fixed at compile time.  The programmer may choose any type which meets the constraint.  The compiler may emit multiple versions of the same function, specialised to each type used, if it so chooses.

Ian Lance Taylor

unread,
Dec 15, 2021, 4:18:43 PM12/15/21
to Brian Hatfield, Jason Phillips, golang-nuts
On Wed, Dec 15, 2021 at 8:29 AM Brian Hatfield <bmhat...@gmail.com> wrote:
>
> I read that and I genuinely do not understand why interfaces now mean two distinct incompatible things. I think this is going to confuse a lot of people.

This is perhaps pedantic, but the two meanings are not incompatible.
The set of interface types that may be used as the types of a variable
are a strict subset of the set of interface types that may be used as
constraints. What is new in 1.18 (among other things) is that it is
now possible to write an interface type that may not be used as the
type of a variable. This is a loss of orthogonality but it doesn't
mean that interface types mean two different things.

It is possible that in the future we will permit variables to have
interface types that use embedded non-interface types, approximation
elements, and union elements. That would be a significant change to
the language and we've chosen to separate that change from the complex
addition of generic support. And we may never make that change. We
don't know yet.

Ian
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CANGiwgY2%2B-xEBzezeh-D_0dv2hQXreWLsiXoOSP%2Bw-LUV%3DkVaw%40mail.gmail.com.

Ian Lance Taylor

unread,
Dec 15, 2021, 4:19:42 PM12/15/21
to Leonard Mittmann, golang-nuts
On Wed, Dec 15, 2021 at 3:24 AM Leonard Mittmann
<leonard....@gmail.com> wrote:
>
Aside from the rest of the conversation, note that your suggestion is
never going to work, because you aren't going to be able to write the
next steps in which you try to instantiate a function with an element
of a slice. You can only instantiate functions with types known at
compile time, not with types known at run time.

Ian

Leonard Mittmann

unread,
Dec 20, 2021, 7:04:48 AM12/20/21
to Ian Lance Taylor, golang-nuts
@Jason Phillips Thanks for pointing me here.

@Ian Lance Taylor I agree with you that both "old"-interfaces and type-interfaces have the same semantics. However, syntactically they are different and this is the reason for my discomfort.

It makes total sense that my code sample won't work even if type-interfaces could be used for variables in the future as the type could not be determined at compile time. But this is very different from the way "old"-interfaces work. In a slice of io.Reader's I can instantiate various types that implement the interface and pass them on to some func consuming an io.Reader.

Maybe this is only me not seeing the bigger picture and possible benefits of reusing interfaces for type constraints. I am curious if there are other cases than my described scenario where this might make total sense. For now I remain skeptical though and find it very confusing trying to fit two syntactically different things in the same term "interface".

Ian Lance Taylor

unread,
Dec 20, 2021, 5:41:20 PM12/20/21
to Leonard Mittmann, golang-nuts
On Mon, Dec 20, 2021 at 4:04 AM Leonard Mittmann
<leonard....@gmail.com> wrote:
>>
>> As to _why_ this is the case, the generics proposal has a section about that:
>> https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#permitting-constraints-as-ordinary-interface-types
>
> @Jason Phillips Thanks for pointing me here.
>
> @Ian Lance Taylor I agree with you that both "old"-interfaces and type-interfaces have the same semantics. However, syntactically they are different and this is the reason for my discomfort.
>
> It makes total sense that my code sample won't work even if type-interfaces could be used for variables in the future as the type could not be determined at compile time. But this is very different from the way "old"-interfaces work. In a slice of io.Reader's I can instantiate various types that implement the interface and pass them on to some func consuming an io.Reader.
>
> Maybe this is only me not seeing the bigger picture and possible benefits of reusing interfaces for type constraints. I am curious if there are other cases than my described scenario where this might make total sense. For now I remain skeptical though and find it very confusing trying to fit two syntactically different things in the same term "interface".

There was years of work getting to this point. For a while we had
constraints (then called contracts) that were distinct from interfaces
(you can see this at
https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-contracts.md).
At that stage we got a lot of feedback of the form "Constraints are an
awful lot like interfaces (see
https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-contracts.md#why-not-use-interfaces-instead-of-contracts).
Why are they distinct? How do we know which one to use?" Changing
constraints to actually be interface types was a significant
simplifying step. We reduced two similar concepts to one.

I want to stress that what you describe as "two syntactically
different things" are only one thing. Interface types used as
constraints are strict supersets of interface types used as variable
types.

Further, It is possible that in the future we will permit all
interface types to be used as variable types. We didn't do that
immediately because we didn't want additions for generics to change
the existing non-generic language. If we do indeed implement that,
then there will clearly be no distinction between the various sorts of
interface types.

Ian
Reply all
Reply to author
Forward
0 new messages