How does one allow a generic type that is convertible to a pointer to parameterise another generic type that is convertible to a pointer?

huangapple go评论124阅读模式
英文:

How does one allow a generic type that is convertible to a pointer to parameterise another generic type that is convertible to a pointer?

问题

从类型参数提案中,有一个有用的部分描述了如何定义类型约束,以便已经通过其指针接收器实现接口的类型仍然可以用作类型参数。例如:

  1. type ExplainedExampleGeneric[T any] interface {
  2. GetBool() bool
  3. *T // 非接口类型约束元素
  4. }
  5. type ExplainedImpl struct{ bully bool }
  6. func (e *ExplainedImpl) GetBool() bool { return e == nil || e.bully }
  7. func Print[T any, PT ExplainedExampleGeneric[T]](impl T) {
  8. fmt.Println(PT(&impl).GetBool())
  9. }
  10. func main() {
  11. Print[ExplainedImpl](ExplainedImpl{}) // 输出:false
  12. }

对于我的用例,我想将ExplainedExampleGeneric作为另一个泛型类型的参数使用:

  1. type UncharteredGeneric[T any, U any, V ExplainedExampleGeneric[U]] interface {
  2. GetString() string
  3. GetExplainedExampleGeneric() V
  4. *T // 非接口类型约束元素
  5. }
  6. type ExplainedExampleGeneric[T any] interface {
  7. GetBool() bool
  8. *T // 非接口类型约束元素
  9. }

这段代码可以编译通过,但是当尝试编译一个使用UncharteredGeneric类型的函数时,会出现以下错误:

  1. ./prog.go:35:40: 2个参数但有3个类型参数
  2. ./prog.go:36:17: 无法将 &impl类型为*T转换为类型PT

最后,当我尝试调用这个函数时,无法推断出我的类型 链接到 playground

  1. ./prog.go:36:40: 2个参数但有3个类型参数
  2. ./prog.go:37:17: 无法将 &impl类型为*T转换为类型PT
  3. ./prog.go:42:50: 无法推断出PTprog.go:36:37

有什么想法我做错了什么吗?似乎应该是可能的,但我不确定我在这里漏掉了什么,非常感谢任何帮助。

英文:

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:

  1. type ExplainedExampleGeneric[T any] interface {
  2. GetBool() bool
  3. *T // non-interface type constraint element
  4. }
  5. type ExplainedImpl struct{ bully bool }
  6. func (e *ExplainedImpl) GetBool() bool { return e == nil || e.bully }
  7. func Print[T any, PT ExplainedExampleGeneric[T]](impl T) {
  8. fmt.Println(PT(&impl).GetBool())
  9. }
  10. func main() {
  11. Print[ExplainedImpl](ExplainedImpl{}) // Prints: false
  12. }

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

  1. type UncharteredGeneric[T any, U any, V ExplainedExampleGeneric[U]] interface {
  2. GetString() string
  3. GetExplainedExampleGeneric() V
  4. *T // non-interface type constraint element
  5. }
  6. type ExplainedExampleGeneric[T any] interface {
  7. GetBool() bool
  8. *T // non-interface type constraint element
  9. }

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

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

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

  1. ./prog.go:36:40: got 2 arguments but 3 type parameters
  2. ./prog.go:37:17: cannot convert &impl (value of type *T) to type PT
  3. ./prog.go:42:50: cannot infer PT (prog.go:36:37)
  1. package main
  2. import "fmt"
  3. type UncharteredGeneric[T any, U any, V ExplainedExampleGeneric[U]] interface {
  4. GetString() string
  5. GetExplainedExampleGeneric() V
  6. *T // non-interface type constraint element
  7. }
  8. type ExplainedExampleGeneric[T any] interface {
  9. GetBool() bool
  10. *T // non-interface type constraint element
  11. }
  12. type UncharteredImpl struct{ some string }
  13. func (e *UncharteredImpl) GetExplainedExampleGeneric() ExplainedImpl { return ExplainedImpl{} }
  14. func (e *UncharteredImpl) GetString() string {
  15. if e == nil {
  16. return ""
  17. }
  18. return e.some
  19. }
  20. type ExplainedImpl struct{ bully bool }
  21. func (e *ExplainedImpl) GetBool() bool { return e == nil || e.bully }
  22. func Print[T any, PT ExplainedExampleGeneric[T]](impl T) {
  23. fmt.Println(PT(&impl).GetBool())
  24. }
  25. func UncharteredPrint[T any, U any, PT UncharteredGeneric[T, U]](impl T) { -> got 2 arguments but 3 type parameters
  26. fmt.Println(PT(&impl).GetExplainedExampleGeneric().GetBool()) -> cannot convert &impl (value of type *T) to type PT
  27. }
  28. func main() {
  29. Print[ExplainedImpl](ExplainedImpl{})
  30. UncharteredPrint[UncharteredImpl, ExplainedImpl](UncharteredImpl{}) -> cannot infer PT (prog.go:36:37)
  31. }

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.

答案1

得分: 2

首先,接口UncharteredGeneric是一个带参数的类型,你必须明确提供所有三个类型参数。

因此,在UncharteredPrint函数的类型参数列表中,PT应该是:

  1. PT UncharteredGeneric[T, U, V]

现在,V是什么?如UncharteredGeneric自己的类型参数列表所示,V必须满足ExplainedExampleGeneric[U],因此我们这样定义它。完整的签名变为:

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

你不能直接使用ExplainedExampleGeneric[U]来实例化UncharteredGeneric,因为ExplainedExampleGeneric包含类型元素*T;你只能将其用作V的约束条件。

最后,在所有这些之后,PT将被推断为*UncharteredImpl,但这还没有实现UncharteredGeneric——V被实例化为*ExplainedImpl,但该方法声明为GetExplainedExampleGeneric() ExplainedImpl。你还需要修复方法签名以返回指针类型:

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

然后,它可以编译和运行:https://go.dev/play/p/zCZd53ys-5J

英文:

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:

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

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

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

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

huangapple
  • 本文由 发表于 2022年4月1日 16:12:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/71703777.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定