Home > Back-end >  Confused by generics - pointer type parameter?
Confused by generics - pointer type parameter?

Time:01-17

I'm confused by generics. I've read a bunch of answers to similar questions and I'm still stumped.

I get that the answer here involves telling the compiler that T and *T are related. That while the implicit type conversion from one to the other happens normally in straight code, that doesn't happen in generics without more code. Could someone please walk me through this?

package main

type Mungeable interface {
    Munge() // a Mungeable is a thing that can be Munge'd
}

type Box[T Mungeable] struct {
    Contents *T // A box is a type based on a Mungeable
}

type Foo struct{} // Foo is a struct...

func (f *Foo) Munge() {} // ...that can be munged.

func (b *Box[Foo]) Print() {
    _ = b.Contents.Munge()
}

produces:

./prog.go:16:17: b.Contents.Munge undefined (type *Foo is pointer to type parameter, not type parameter)

Go build failed.

what do I need to change to get this to compile?

playground here: https://go.dev/play/p/gNqxWhlmWmz

CodePudding user response:

Mungable is an interface, a pointer to an interface has no method-set (unlike a pointer to concrete type, which has a method-set containing the methods of its base type in addition to whatever methods were declared for the pointer type), so you need to dereference the pointer before calling the interface's method.

Another issue is that Munge does not have a return value, therefore the assignment statement _ = ...Munge() will cause a compile time error.

The following works:

package main

type Mungeable interface {
    Munge() // a Mungeable is a thing that can be Munge'd
}

type Box[T Mungeable] struct {
    Contents *T // A box is a type based on a Mungeable
}

type Foo struct{} // Foo is a struct...

func (f *Foo) Munge() {} // ...that can be munged.

func (b *Box[Foo]) Print() {
    (*b.Contents).Munge()
}

func main() {}

https://go.dev/play/p/HWW6k47Cdvh

CodePudding user response:

Your understanding of methods and interfaces is wrong. The method set of Foo includes those that defined with the receiver of type Foo, while the method set of *Foo includes methods defined with the receiver of both types Foo and *Foo. Given these declarations

type Mungeable interface {
    Munge()
}

type Foo struct {}

func (f *Foo) Munge() {}

Then Foo does not implement Mungeable, it is *Foo that does so. This is reflected in the fact that you cannot assign a value of type Foo to a variable of type Mungeable, and var x Mungeable = new(Foo) is fine. On the contrary, if your Munge method is defined as so

func (f Foo) Munge() {}

Then both Foo and *Foo would implement Mungeable.

The second mistake of yours is that func (f *Box[Foo]) Print() does not define a method with the receiver of type Box[Foo], it defines a method with the receiver of generics type Box with the type parameter declared as Foo. In other words, it is the same as func (f *Box[T]) Print(). If you want to act on Box[Foo] only, then I think you must put it in the parameter list, as I don't think Go allows specialisation of generics classes.

The solution to your problem is that you can just make Contents of type T and change type parameter to *Foo, like this:

package main

type Mungeable interface {
    Munge() // a Mungeable is a thing that can be Munge'd
}

type Box[T Mungeable] struct {
    Contents T // A box is a type based on a Mungeable
}

type Foo struct{} // Foo is a struct...

func (f *Foo) Munge() {} // ...that can be munged.

func Print(b *Box[*Foo]) {
     b.Contents.Munge()
}
  • Related