2

Golang 如何使用 struct 泛型?

 2 years ago
source link: https://www.v2ex.com/t/835462
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  Go 编程语言

Golang 如何使用 struct 泛型?

  blue7wings · 8 小时 51 分钟前 · 938 次点击

type A struct {
	AID string
}
type B struct {
	BID string
}

type AB interface {
	A | B
}

func Get[val AB]() val {
	return A{
		AID: "AID",
	}
}

定义了两个 struct ,A 和 B ,并用定义了一个 constraint 包含了 A ,B 两个 struct ,那么 Get 函数返回 A ,为什么会提示"cannot use (A literal) (value of type A) as val value in return"?

刚刚接触 go 的泛型,还不是特别理解,网上也没搜到相关问题,请教一下大家,这里是哪里的错误?

16 条回复    2022-02-21 22:01:29 +08:00

imkerberos

imkerberos      8 小时 29 分钟前   ❤️ 1

proxytoworld

proxytoworld      8 小时 18 分钟前

楼主代码确定没问题吗,为什么我报错了

GM

GM      8 小时 17 分钟前

大道至简 /go 头

janxin

janxin      7 小时 50 分钟前

因为这里泛型不能这么用...

定义 A|B 的时候不是或关系,而是需要 AB 都满足相同约束,你这个地方是不满足的。甚至你把 B 改成

type B struct {
AID string
BID string
}

都是不行的... 原因是 A 不满足 BID 约束

dcalsky

dcalsky      6 小时 59 分钟前

@janxin
type A struct {
AID string
BID string
}

type B struct {
AID string
BID string
}

这样也是不行的

thevita

thevita      6 小时 54 分钟前

@janxin

func Show[val AB](v val) {
fmt.Println(v)
}

func main() {
a := A{AID: "aid"}
b := B{BID: "bid"}
Show(a)
Show(b)
}

----
约束应该是可以这么用的

还没认真看过 go 的范型,所以不是很了解

大概逻辑是,范型展开的时候,需要根据具体的 代码(及 调用 Get/Show 的代码)中的类型信息( concrete type )进行约束检查,并展开成 concrete type 的代码, 不能用具体返回值来推断 函数返回类型 不然如下代码应该怎么办呢

----
func FuncA[val AB](yes bool) val {
if yes {
return A{
AID: "aid"
}
} else {
return B{
BID: "bid"
}
}
}
----

eastphoton

eastphoton      6 小时 40 分钟前

你想用的是多态吧。。。

eastphoton

eastphoton      6 小时 23 分钟前

type A struct {
AID string
}
type B struct {
BID string
}

type AB interface {
}

func Get() AB {
return A{
AID: "AID",
}
}

// -------------------

type AX struct {
AID string
}
type BX struct {
AID string
}

type ABX interface {
AX | BX
}

func GetX[val ABX]() val {
return val{
AID: "AID",
}
}

// -------------------

func main() {
fmt.Println(Get())
fmt.Println(GetX[AX](), GetX[BX]())
}

ZSeptember

ZSeptember      6 小时 0 分钟前

union 不支持 struct ,只支持基本类型

thevita

thevita      5 小时 44 分钟前

@ZSeptember 测试了下,应该是支持的,只不过很鸡肋,貌似没啥卵用。

```
package main

import (
"fmt"
"runtime"
)

type A struct {
AID string
}

type B struct {
BID string
}

type AB interface {
A | B
}

func Show[val AB](v val) {
pc := make([]uintptr, 10) // at least 1 entry needed
runtime.Callers(0, pc)
f := runtime.FuncForPC(pc[0])
fmt.Printf("%s => %x\n", f.Name(), f.Entry())
fmt.Println(v)
}

func main() {
a := A{AID: "aid"}
b := B{BID: "bid"}
Show(a)
Show(b)
Show(A{AID: "test"})

}

```

=====
main.Show[...] => 108b0a0
{aid}
main.Show[...] => 108b280
{bid}
main.Show[...] => 108b0a0
{test}
=====

如上, A, B 两个类型,展开成了两个,Show 函数, 不过 貌似 v val 在 Show 里面什么都做不了,如果要转型成 A 或者 B 需要用 反射,要这范型何用。

请哪位大佬解惑

janxin

janxin      5 小时 30 分钟前

@thevita 因为你这个地方的约束是用的 fmt.Stringer

上面的程序中直接 return 一个 var v val 就会成功,但是你直接 return 了特定类型,相当于缩小了这个约定。

@dcalsky 抱歉这个地方忘记指明一个地方要调整了,就是上面说的那个原因。这种你这个具体例子中的情况可以使用下面的返回:

return val{
AID: "AID",
}

janxin

janxin      5 小时 29 分钟前

@janxin 具体应该不是这个约束...记不太清具体是那个了

thevita

thevita      5 小时 18 分钟前

@janxin

不是的,不是实例化 Stringer 类型

那个 binary 的符号表如下(过滤了下)

Show 对 A B 两个类型有两个实例

[![HvvkV0.jpg]( https://s4.ax1x.com/2022/02/21/HvvkV0.jpg)]( https://imgtu.com/i/HvvkV0)

janxin

janxin      5 小时 12 分钟前

@thevita 你这是编译完成后展开之后的情况,编译成功之后就会被展开。我说的是编译期间约束检查。

janxin

janxin      5 小时 2 分钟前

@thevita 换句话说,对于#6 中的例子中,你使用 fmt.Println(v.AID)是可以编译成功的,但是对顶楼和#7 例子结合的情况,编译是无法通过的。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK