Generic interface compiles with incorrectly instantiated types

184 views
Skip to first unread message

Tsvi Benschar

unread,
Jun 20, 2022, 6:27:13 PM6/20/22
to golang-nuts
Hey everyone, reposting a question I asked on the go forum since it isn't getting replies there. I have some code that’s compiling and running despite there being a type error. I don’t understand go generics and interfaces that well, so I don’t know if this behavior is intended.

Here's a simple incorrect program that compiles. (Here's the correct version.) On line 23 of the incorrect version, the function's generic parameter is instantiated to string, but its input is of type int. It compiles anyways and panics at runtime, even though the type switch's default case should never be reached.

I got some similar examples to compile and run, they all involve instantiating a generic interface with one type but using it as if it had a different type. Is this behavior intended, or maybe is there some implicit unsafe cast happening? Or is this a bug?

Ian Lance Taylor

unread,
Jun 20, 2022, 6:49:10 PM6/20/22
to Tsvi Benschar, golang-nuts
Thanks for providing a complete test case.

The Wrap interface ignores its type parameter, so any type that
implements a isWrap method with no arguments and no results will
implement Wrap with any type argument. The argument to Extract is
Wrap[A], so you can pass anything with an isWrap method to Extract.
The type Val[int] does have an isWrap method, so it's fine to pass
that type to Extract[int].

I don't understand what your code is trying to do, but the behavior
seems entirely correct to me.

Ian

Michel Levieux

unread,
Jun 21, 2022, 11:07:01 AM6/21/22
to Tsvi Benschar, golang-nuts
Hi,

I'm not a generics expert either, but I think there is a misunderstanding here:

The A in the Wrap[A any] is not the same as the A in Val[A any]. When you instantiate the Extract function with Extract[string], it expects a Wrap[string], which is, in fact, just a type implementing isWrap. The Val[A any] implements this method whatever the type it is instantiated with (string, int...) so Extract[string](Val[int]{3}) is, from my understanding, a completely valid expression. The only thing we can be certain of with this code is that the type switch will **not** reach the default case only precisely when the Val[A any] is instantiated with the same type as Extract[A any] function.

Hope I'm not getting anything wrong :)

--
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/511571a9-044f-4a9b-8c5c-ed48c98206a5n%40googlegroups.com.

Brian Candler

unread,
Jun 21, 2022, 11:42:49 AM6/21/22
to golang-nuts
Maybe it's clearer if you simplify Wrap[A] to just Wrap, since they are identical.  https://go.dev/play/p/aJReo5lp2Yg

Then it's clear that the parameter "w Wrap" to func Extract is just a plain old interface type, unrelated to generics.  Dynamically at runtime it can be nil, or a value of any type which implements the Wrap interface.

Interfaces aren't implemented by structural inheritance, they're just duck-typing: "does the concrete type implement this method set?"  Therefore, these interfaces are identical and interchangeable:

type Wrap1 interface {
    isWrap()
}

type Wrap2 interface {
    isWrap()
}

type Wrap3[A any] interface {
    isWrap()
}

You can pass a value of type Wrap2 to a function which expects type Wrap1.  https://go.dev/play/p/AabV-XGfj1g

Christoph Berger

unread,
Jun 21, 2022, 1:38:40 PM6/21/22
to golang-nuts
> The Wrap interface ignores its type parameter

Curious, why doesn't the compiler consider this an error? I would expect a "type parameter declared but not used" error, similar to the "variable declared but not used" error.
Or are there valid use cases for types that declare a type parameter but do not use it?

Bruno Albuquerque

unread,
Jun 21, 2022, 2:14:51 PM6/21/22
to Christoph Berger, golang-nuts
One reason is that then your type can have a method like "Convert() T" where the actual type is not relevant (i.e. you do not need to actually have a generic type associated with it) but you can have a function that returns a specific type anyway. As methods can not have extra type parameters, this would be the only way to do something like that.


--
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.

Tsvi Benschar

unread,
Jun 21, 2022, 4:19:29 PM6/21/22
to golang-nuts
Thanks for the response, this makes sense wrt duck typing.

>  I don't understand what your code is trying to do...

The code I posted was a minimal working example. In my original use case I was trying to emulate a generic sum type, where the sum type was an interface with an unexported dummy method implemented by each of its cases. So I thought that eg a maybe type could look like this. As I understand it now, if I wanted a function with a parameter Maybe[int] to exclude an input of Some[string], I could do something like this instead. I'm now realizing that even that wouldn't fully behave as I wanted because it doesn't account for nil values, so I'll have to figure out a better solution.

Christoph Berger

unread,
Jun 21, 2022, 4:47:48 PM6/21/22
to golang-nuts
> One reason is that then your type can have a method like "Convert() T"

But then the type parameter is not unused anymore.

Brian Candler

unread,
Jun 22, 2022, 5:12:12 AM6/22/22
to golang-nuts
On Tuesday, 21 June 2022 at 21:19:29 UTC+1 tsvihb...@gmail.com wrote:
I'm now realizing that even that wouldn't fully behave as I wanted because it doesn't account for nil values, so I'll have to figure out a better solution.

For a "Maybe" type specifically, the nil interface value could serve as your "None" value:

For other union types, it's possible to use generics own union types, with a trick:
Reply all
Reply to author
Forward
0 new messages