英文:
generics with a T type and a named function type that takes T
问题
我有一个定义了一堆类型的模块:
type Thing1 struct {}
type Thing2 struct {}
还有以上述类型作为参数的命名函数类型:
type T1 func(t *Thing1)
type T2 func(t *Thing2)
然后,它使用这些函数类型定义了一个映射:
var (
ModThing1 = map[string]T1{ ... }
ModThing2 = map[string]T2{ ... }
)
在使用这个模块的应用程序中,我想要为Thing1
和Thing2
使用泛型。
类似这样:
func do[T any](in *T, inMap map[string]func(in *T)) {
for _, val := range inMap {
val(in)
}
}
...
do[mod.Thing1](&mod.Thing1{}, mod.ModThing1)
当然问题是,Go不允许这样做,因为映射的值类型与mod.ModThing1
的值类型不同。func(in *T))
与mod.T1
不同。
有办法让这个工作吗?
英文:
I have a module that defines a bunch of types
type Thing1 struct {}
type Thing2 struct {}
and named functions types that take the types above as arguments
type T1 func(t *Thing1)
type T2 func(t *Thing2)
Then it defines a map using these function types
var (
ModThing1 = map[string]T1{ ... }
ModThing2 = map[string]T2{ ... }
)
In my app that uses this module, I would like use a generic for Thing1
and Thing2
Something like:
func do[T any](in *T, inMap map[string]func(in *T)) {
for _, val := range inMap {
val(in)
}
}
...
do[mod.Thing1](&mod.Thing1{}, mod.ModThing1)
Of course the problem is that Go wont allow this because the type of the value of the map is not the same as mod.ModThing1
value type. func(in *T))
vs mod.T1
Is there a way to get this to work?
答案1
得分: 2
在函数do
中,声明一个额外的类型参数F
,其约束条件近似地引用了T
。你可以利用T1
和T2
具有相似的底层类型。你甚至不需要显式实例化do
的类型参数,两者都可以被推断出来。
func main() {
do(&foo.Thing1{}, foo.ModThing1)
do(&foo.Thing2{}, foo.ModThing2)
}
func do[T any, F ~func(*T)](in *T, inMap map[string]F) {
for _, val := range inMap {
val(in)
}
}
Playground: https://go.dev/play/p/wiWWzXVDG7v
如果你对导入的包有控制权,另一种解决方案是使用泛型函数类型代替T1
和T2
。这个抽象的原理是相同的。实际上,在这里你可以更清楚地看到为什么第一个解决方案有效:
type F[T any] func(t *T)
// 替代
// type T1 func(t *Thing1)
// type T2 func(t *Thing2)
然后,你使用特定实例化的F[T]
声明map变量:
var (
ModThing1 = map[string]F[Thing1]{ ... }
ModThing2 = map[string]F[Thing2]{ ... }
)
然后在do
函数中,你用类型参数实例化F
:
func do[T any](in *T, inMap map[string]F[T]) {
for _, val := range inMap {
val(in)
}
}
Playground: https://go.dev/play/p/ITBqiqjjVUz
顺便说一下,你还可以对map类型进行抽象:
type M[T any] map[string]F[T]
func do[T any](in *T, inMap M[T]) {
for _, val := range inMap {
val(in)
}
}
英文:
In the function do
, declare an additional type parameter F
with an approximated constraint that also references T
. You take advantage of T1
and T2
having similar underlying types. You don't even have to explicitly instantiate do
's type arguments, both can be inferred.
func main() {
do(&foo.Thing1{}, foo.ModThing1)
do(&foo.Thing2{}, foo.ModThing2)
}
func do[T any, F ~func(*T)](in *T, inMap map[string]F) {
for _, val := range inMap {
val(in)
}
}
Playground: https://go.dev/play/p/wiWWzXVDG7v
<hr>
If you have control over the imported package, another solution is to use a generic function type instead of T1
and T2
. The principle behind this abstraction is the same. In fact, here you can see more clearly why the first solution works:
type F[T any] func(t *T)
// instead of
// type T1 func(t *Thing1)
// type T2 func(t *Thing2)
Then you declare the map variables with specific instantiations of F[T]
:
var (
ModThing1 = map[string]F[Thing1]{ ... }
ModThing2 = map[string]F[Thing2]{ ... }
)
Then in the do
function you instantiate F
with the type parameter:
func do[T any](in *T, inMap map[string]F[T]) {
for _, val := range inMap {
val(in)
}
}
Playground: https://go.dev/play/p/ITBqiqjjVUz
<hr>
By the way, you can also abstract the map type:
type M[T any] map[string]F[T]
func do[T any](in *T, inMap M[T]) {
for _, val := range inMap {
val(in)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论