Issue
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.
Solution
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 UncharteredGeneric
— V
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
Answered By – blackgreen
Answer Checked By – David Goodson (GoLangFix Volunteer)