如何在Go中正确地为泛型值设置多个类型?

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

How to set multiple types for generic values in Go correctly?

问题

我想在Golang中为哈希映射的值设置多个类型。我实现了Golang泛型any,并编写了一个返回map[string]any的函数。

然而,当我运行代码后,它返回了以下错误信息:

$ cannot use r.Method (variable of type string) as type T in map literal

在Go中,为哈希映射的值设置多个类型的正确方法是什么?

以下是我的代码:

package main

type RequestProperty struct {
	Method string
	Params []any
	Id     int
}

func SetRequestProperty[T any](payloadCombine bool) map[string]T {
	var p map[string]T
	var r = RequestProperty{
		Method: "SET_PROPERTY",
		Params: []any{"combined", payloadCombine},
		Id:     5,
	}
    // 仅用于测试
	p = map[string]T{
		"method": r.Method,  // << 这里出错了
	}

	return p
}

func main() {
    p := SetRequestProperty(true)
}

[编辑]
尽管如此,这似乎是有效的...我不知道为什么。

package main

type RequestProperty struct {
	Method string
	Params []any
	Id     int
}

// 删除 [T any],map[string]T
// 将其改为 map[string]any
func SetRequestProperty(payloadCombine bool) map[string]any {
	var p map[string]any
	var r = RequestProperty{
		Method: "SET_PROPERTY",
		Params: []any{"combined", payloadCombine},
		Id:     5,
	}
    // 仅用于测试
	p = map[string]any{
		"method": r.Method,
	}

	return p
}

func main() {
    p := SetRequestProperty(true)
}

T不应该只是类型any的别名吗?我是否理解错了什么?

英文:

I want to set multiple types for values of hashmap in Golang. I implemented golang generics any, and wrote function that returned map[string]any.

However, after I ran the code, it returned

$ cannot use r.Method (variable of type string) as type T in map literal

What is the correct way of setting multiple types for values of hashmap in Go?

Here's my code

package main

type RequestProperty struct {
	Method string
	Params []any
	Id     int
}

func SetRequestProperty[T any](payloadCombine bool) map[string]T {
	var p map[string]T
	var r = RequestProperty{
		Method: &quot;SET_PROPERTY&quot;,
		Params: []any{&quot;combined&quot;, payloadCombine},
		Id:     5,
	}
    // just for test
	p = map[string]T{
		&quot;method&quot;: r.Method,  // &lt;&lt; Error Here
	}

	return p
}

func main() {
    p := SetRequestProperty(true)
}

[EDIT]
This seems to be working though... I don't know why.

package main

type RequestProperty struct {
	Method string
	Params []any
	Id     int
}

// delete [T any], map[string]T 
// change it to map[string]any
func SetRequestProperty(payloadCombine bool) map[string]any {
	var p map[string]any
	var r = RequestProperty{
		Method: &quot;SET_PROPERTY&quot;,
		Params: []any{&quot;combined&quot;, payloadCombine},
		Id:     5,
	}
    // just for test
	p = map[string]any{
		&quot;method&quot;: r.Method,
	}

	return p
}

func main() {
    p := SetRequestProperty(true)
}

Shouldn't T just act like an alias to type any? Am I misunderstanding something?

答案1

得分: 2

> T 应该只是一个别名,指向 any 类型吗?

不,不应该。

T 是一个类型参数,而不是 any。它只是受到 any 的约束。

更一般地说:类型参数并不等同于它的约束。

每次泛型函数被实例化时,T 都会被赋予一个具体的类型参数,该参数满足其约束条件。在函数体内部,map[string]T 就变成了一个从 string 到具体 T 类型的映射。

p := SetRequestProperty[int](true)
// 使得 body 看起来像:`var p map[string]int`
// 字面量 `map[string]int{"method": r.Method}` 显然是无法工作的

因此,在编译时,编译器会拒绝对 T 进行不与 T 类型集中的所有类型兼容的赋值。代码 map[string]T{"method": r.Method} 无法编译,因为:

  • T 受到 any 的约束,所以它的类型集包括任何类型
  • r.Method 的类型是 string,而 string 无法赋值给任何类型。

<hr>

相反,如果使用 map[string]anyany 并不是作为约束使用,而是作为静态类型,它是 interface{} 的别名,所有类型都可以赋值给空接口。

如果你想要拥有具有不同运行时类型的容器,使用 any 作为静态类型,如 map[string]any,是唯一的方法。如果要限制允许的类型,可以使用基本接口而不是类型参数。

此外,还可以参考这里的最高票答案:https://stackoverflow.com/questions/71887083/what-are-the-benefits-of-replacing-an-interface-argument-with-a-type-parameter

英文:

> Shouldn't T just act like an alias to type any?

No, it shouldn't.

T is a type parameter, not any. It is only constrained by any.

More generally: a type parameter is not its constraint.

Each time a generic function is instantiated, T is assigned a concrete type argument — which satisfies its constraint — and within the function body, map[string]T becomes a map from string to whatever the concrete T is.

p := SetRequestProperty[int](true)
// makes body look like: `var p map[string]int`
// the literal `map[string]int{&quot;method&quot;: r.Method}` obviously can&#39;t work

Therefore at compile time, the compiler will reject assignments to T that are not compatible with all types in T's type set. The code map[string]T{&quot;method&quot;: r.Method} doesn't compile because:

  • T is constrained by any, so its type set comprises anything
  • r.Method is of type string, and string isn't assignable to anything.

<hr>

With map[string]any instead any is not used as a constraint, it is used as a static type, which is an alias of interface{} and all types are always assignable to the empty interface.

If you want to have a container with different runtime types, using any as a static type as in map[string]any is the only way. To restrict allowed types, use basic interfaces instead of type parameters.

See also the top-voted answer here: https://stackoverflow.com/questions/71887083/what-are-the-benefits-of-replacing-an-interface-argument-with-a-type-parameter

huangapple
  • 本文由 发表于 2022年6月29日 08:52:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/72794634.html
匿名

发表评论

匿名网友

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

确定