将 func(T) 转换为 func(any)。

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

Convert func(T) to func(any)

问题

我想要能够在结构体的两个字段之间强制实现相似性,但同时又能够在映射或切片中有多个这样的结构体。

这是我问题的一个简化示例:

package main

type foo[T any] struct {
    f func() T
    v T
}

type bar struct {
    x []*foo[any]
}

func baz[T any](b *bar, f func() T) {
    b.x = append(b.x, &foo[any]{f: f})
}

func main() {
    var b bar
    baz(&b, func() int { return 0 })
}

编译器报错:

./prog.go:13:33: cannot use f (variable of type func() T) as type func() any in struct literal

有趣的是,如果我不需要在结构体中有一个函数指针,这个问题就可以解决。参见https://go.dev/play/p/qXTmaa9PuVe

那么,有没有办法将 T 转换为 any?

我知道可以使用 interface{} 并使用反射来实现我想要的效果,但我相信只使用泛型也是可能的。

如果有办法解决我的问题,请考虑到我正在制作一个 flag 包的背景。重要的结构体如下:

type flag[T any] struct {
    value T
    parse func(in string) (T, error)
    // 其他字段已省略...
}

type FlagSet struct {
    // flag[any] 是否可以用更好的答案替代?
    flags map[string]*flag[any]
    // 其他字段已省略...
}

问题已关闭,所以我必须在这里回答我问题的第二部分:

flag[any] 是否可以用更好的答案替代?

上述问题的答案是肯定的。

解决方案:

最初我认为可以这样做:"一个 func() 可以适应 func(),而一个 any 可以适应 T,那么为什么不能让 func() T 适应 func() any 呢?" 当然,原因是 func() any 不是 any,因此它不能保存 func() T

相反,你可以这样做:

package main

type foo[T any] struct {
    f func() T
    v T
}

func (f *foo[_]) set() {
    f.v = f.f()
}

type anyfoo interface {
    set()
}

type bar struct {
    x []anyfoo
}

func baz[T any](b *bar, f func() T) {
    b.x = append(b.x, &foo[T]{f: f})
}

func main() {
    var b bar
    baz(&b, func() int { return 0 })
}
英文:

I want to be able to enforce similarity between two fields of a struct but also have several of these structs in a map or slice.

Here's a simplified example of my problem:

package main

type foo[T any] struct {
    f func() T
    v T
}

type bar struct {
    x []*foo[any]
}

func baz[T any](b *bar, f func() T) {
    b.x = append(b.x, &foo[any]{f: f})
}

func main() {
    var b bar
    baz(&b, func() int { return 0 })
}

The compiler complains
>./prog.go:13:33: cannot use f (variable of type func() T) as type func() any in struct literal

The funny thing is that this can work if I didn't need to have a function pointer in the struct. See https://go.dev/play/p/qXTmaa9PuVe

So, is there a way for me to turn a T into an any?

I know I could do this with interface{}s and use reflect to enforce what I want, but I'm sure it's possible with only generics.

The context in case there is a way around my problem is that I'm making a flag package. The important structs look like this:

type flag[T any] struct {
    value T
    parse func(in string) (T, error)
    // Other fields removed for simplicity...
}

type FlagSet struct {
    // could flag[any] be replaced with a better answer?
    flags map[string]*flag[any]
    // Other fields removed for simplicity...
}

The question was closed so I have to put the answer to the second part of my question here
>could flag[any] be replaced with a better answer?

The answer to the above is yes.

Solution:

Originally I though something like: "a func() fits a func() and an any fits a T so why can't I have a func() T fit a func() any?" Of course the reason is a func() any is not an any and so it cannot hold a func() T.

Instead, you can do the following:

package main

type foo[T any] struct {
    f func() T
    v T
}

func (f *foo[_]) set() {
    f.v = f.f()
}

type anyfoo interface {
    set()
}

type bar struct {
    x []anyfoo
}

func baz[T any](b *bar, f func() T) {
    b.x = append(b.x, &foo[T]{f: f})
}

func main() {
    var b bar
    baz(&b, func() int { return 0 })
}

答案1

得分: 1

> 但是我也想在一个映射或切片中拥有几个这样的结构体

你不能以类型安全的方式做到这一点。例如,切片的所有值必须具有相同的元素类型。如果你想存储不同类型的值,你必须使用interface{}并在后面进行类型切换。

如果你使用正确的技术术语"参数多态"来代替"泛型",你会明白为什么func(T)func(any)是不同的、不可转换的类型。

> 那么,有没有办法将T转换为any?

没有,既没有"泛型"之前的方法,也没有"泛型"之后的方法。将T转换为any可以理解为Go允许的"类型转换"和"赋值"。你可以将任何类型为T的变量赋值给类型为any的变量。

你可以通过使用适配器函数(闭包)来解决这个问题:

w := func(a any){ f(a.(T)) }
b.x = append(b.x, &foo[any]{w})
英文:

> but also have several of these structs in a map or slice

You cannot do this (in a type safe way). All values of e.g. a slice must have the same element type. If you want to store different ones you have to resort to interface{} and type switch later.

If you use the correct technical term parametric polymorphism instead of "generics" which doesn't explain what is going on you will see why func(T) and func(any) are different, unconvertable types.

> So, is there a way for me to turn a T into an any?

No, there was no pre-"generics" way and there is no post-"generics" way. It helps to think of "turn into" as what Go allows: "type conversion" and "assignment". You can assign any variable of type T to a variable of type any.

You might overcome your issue by using an adaptor function (closure):

w := func(a any){ f(a.(T)) }
b.x = append(b.x, &foo[any]{w})

huangapple
  • 本文由 发表于 2022年4月19日 11:35:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/71919380.html
匿名

发表评论

匿名网友

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

确定