Home > database >  How does one allow a generic type that is convertible to a pointer to parameterise another generic t
How does one allow a generic type that is convertible to a pointer to parameterise another generic t

Time:04-03

From the type parameters proposal, there is a useful section describing how one should define a type constraint such that a type that already implements the interface via it's pointer receivers can still be used as a type parameter, for example:

type ExplainedExampleGeneric[T any] interface {
    GetBool() bool
    *T // non-interface type constraint element
}

type ExplainedImpl struct{ bully bool }

func (e *ExplainedImpl) GetBool() bool { return e == nil || e.bully }

func Print[T any, PT ExplainedExampleGeneric[T]](impl T) {
    fmt.Println(PT(&impl).GetBool())
}

func main() {
    Print[ExplainedImpl](ExplainedImpl{}) // Prints: false
}

For my use-case, I want to use this ExplainedExampleGeneric as a parameter to another generic type:

type UncharteredGeneric[T any, U any, V ExplainedExampleGeneric[U]] interface {
    GetString() string
    GetExplainedExampleGeneric() V
    *T // non-interface type constraint element
}

type ExplainedExampleGeneric[T any] interface {
    GetBool() bool
    *T // non-interface type constraint element
}

This compiles, however, when trying to compile a function to utilise this UncharteredGeneric type, I get the following errors:

./prog.go:35:40: got 2 arguments but 3 type parameters
./prog.go:36:17: cannot convert &impl (value of type *T) to type PT
func UncharteredPrint[T any, U any, PT UncharteredGeneric[T, U]](impl T) { -> got 2 arguments but 3 type parameters
    fmt.Println(PT(&impl).GetExplainedExampleGeneric().GetBool()) -> cannot convert &impl (value of type *T) to type PT
}

Finally, when I try to call this function, my type is unable to be inferred link to playground:

./prog.go:36:40: got 2 arguments but 3 type parameters
./prog.go:37:17: cannot convert &impl (value of type *T) to type PT
./prog.go:42:50: cannot infer PT (prog.go:36:37)
package main

import "fmt"

type UncharteredGeneric[T any, U any, V ExplainedExampleGeneric[U]] interface {
    GetString() string
    GetExplainedExampleGeneric() V
    *T // non-interface type constraint element
}

type ExplainedExampleGeneric[T any] interface {
    GetBool() bool
    *T // non-interface type constraint element
}

type UncharteredImpl struct{ some string }

func (e *UncharteredImpl) GetExplainedExampleGeneric() ExplainedImpl { return ExplainedImpl{} }
func (e *UncharteredImpl) GetString() string {
    if e == nil {
        return ""
    }
    return e.some
}

type ExplainedImpl struct{ bully bool }

func (e *ExplainedImpl) GetBool() bool { return e == nil || e.bully }

func Print[T any, PT ExplainedExampleGeneric[T]](impl T) {
    fmt.Println(PT(&impl).GetBool())
}

func UncharteredPrint[T any, U any, PT UncharteredGeneric[T, U]](impl T) { -> got 2 arguments but 3 type parameters
    fmt.Println(PT(&impl).GetExplainedExampleGeneric().GetBool()) -> cannot convert &impl (value of type *T) to type PT
}

func main() {
    Print[ExplainedImpl](ExplainedImpl{})
    UncharteredPrint[UncharteredImpl, ExplainedImpl](UncharteredImpl{}) -> cannot infer PT (prog.go:36:37)
}

Any idea what I'm doing wrong? Seems like it should be possible but I'm unsure of what I'm missing here, any help would be greatly appreciated.

CodePudding user response:

First, the interface UncharteredGeneric is a parametrized type, you must explicitly supply all three type parameters.

Therefore in UncharteredPrint function type parameter list, PT should be:

PT UncharteredGeneric[T, U, V]

Now what is V? As seen in UncharteredGeneric own type param list, V must satisfy ExplainedExampleGeneric[U], hence this is how we define it. The full signature becomes:

UncharteredPrint[T any, U any, V ExplainedExampleGeneric[U], PT UncharteredGeneric[T, U, V]](impl T)

You can't use directly ExplainedExampleGeneric[U] to instantiate UncharteredGeneric because ExplainedExampleGeneric includes the type element *T; you can only use it as a constraint for V.

Finally, after all this PT would be inferred as *UncharteredImpl, but this doesn't yet implement UncharteredGenericV is instantiated with *ExplainedImpl, but the method is declared as GetExplainedExampleGeneric() ExplainedImpl. You have also to fix the method signature to return the pointer type:

func (e *UncharteredImpl) GetExplainedExampleGeneric() *ExplainedImpl { return &ExplainedImpl{} }

Then, it compiles and runs: https://go.dev/play/p/zCZd53ys-5J

  • Related