有一个 Golang 泛型的问题咨询

2023-02-14 17:31:22 +08:00
 aapeli

为啥下面的 Golang 代码无法正常工作, 我理解两个 struct Test 和 TextNext 不是都有 V 这个变量么?

package main

import "fmt"

type Test struct {
	V string
}

type TestNext struct {
	V string
}

func handle[T Test | TestNext](a T) {
	fmt.Println(a.V) // 这里会报错. 提示 未解析的引用 'V'
}

func main() {
	handle(Test{V: "Hello"})
}

2542 次点击
所在节点    Go 编程语言
39 条回复
yangheng4922
2023-02-14 17:37:36 +08:00
感觉应该是编译器不知道 a 是什么类型的吧
yangheng4922
2023-02-14 17:41:54 +08:00
![Snipaste_2023-02-14_17-40-35.jpg]( https://img1.imgtp.com/2023/02/14/DMsFW93P.jpg)
aapeli
2023-02-14 17:44:57 +08:00
@yangheng4922 AI 给出的是 非泛型的写法,有泛型的么?
Nazz
2023-02-14 17:51:28 +08:00
访问具体的数据要用接口约束;

package main

import "fmt"

type Test struct {
V string
}

func (t *Test) GetValue() string {
return t.V
}

type TestNext struct {
V string
}

func (t *TestNext) GetValue() string {
return t.V
}

type Value interface {
GetValue() string
}

func handle[V Value](a V) {
fmt.Println(a.GetValue())
}

func main() {
handle(&Test{V: "Hello"})
}
timethinker
2023-02-14 17:56:20 +08:00
我没用过 Go 的泛型,但是从实际使用的场景来看,Test 和 TestNext 是没有任何关系的,虽然都有一个成员 V 并且类型一致,但是编译器并不能直接将两者进行关联,所以重点是要让 Test 和 TestNext 产生关系,可以改造一下使这两个都实现同一个合同或者接口(如果 Go 支持其中之一的话)。
Mexion
2023-02-14 18:08:01 +08:00
Go 只认接口
GogoGo666
2023-02-14 18:11:54 +08:00
@yangheng4922 #2 ChatGPT 了解的 go 的最新版本是 1.16
lawlielt
2023-02-14 18:15:14 +08:00
g1.18 release notes 有表述
The Go compiler does not support accessing a struct field x.f where x is of type parameter type even if all types in the type parameter's type set have a field f. We may remove this restriction in a future release.
DefoliationM
2023-02-14 18:47:49 +08:00
看看 go 泛型的文档,写个接口。
aapeli
2023-02-14 18:52:56 +08:00
感谢各位 @DefoliationM @Mexlon @timethinker @yangheng4922
最终我选择了如下的写法, 为什么转成 any 类型,不直接使用 any 作为参数 a 的类型?
因为我想只限制使用 Test | TestNext 这两个类型, 这样 handle 函数只能传指定的类型.
参考: https://stackoverflow.com/questions/73864711/get-type-parameter-from-a-generic-struct-using-reflection


```
package main

import "fmt"

type Test struct {
V string
}

type TestNext struct {
V string
}

type Interface interface {
Test | TestNext
}

func handle[T Interface](a T) {
switch val := any(a).(type) {
case Test:
fmt.Println(val.V)
case TestNext:
fmt.Println(val.V)
}
}

func main() {
handle(Test{V: "Hello"})
}

```
rrfeng
2023-02-14 19:28:32 +08:00
可以用复合接口满足你这个需求:

type MyType interface {
Test | TestNext // 类型约束
getV() string // 方法约束
}

就不用做 type assertion 了
liuxu
2023-02-14 19:34:23 +08:00
我建议你按 4 楼的来,go 本来就是鸭子模型,范也得范鸭子的模型
10 楼你硬是写成的 php 动态类型验证,看似用了范型,实际上没用,你把[xxx]删了,T 改成 interface{}也行

package main

import "fmt"

type Test struct {
V string
}

type TestNext struct {
V string
}

func handle(a interface{}) {
switch val := any(a).(type) {
case Test:
fmt.Println(val.V)
case TestNext:
fmt.Println(val.V)
}
}

func main() {
handle(Test{V: "Hello"})
}
hahadaxigua834
2023-02-14 19:58:52 +08:00
go 现在不支持这种写法,https://github.com/golang/go/issues/48522
rrfeng
2023-02-14 20:07:30 +08:00
@hahadaxigua834
这个 issue 里提供了十几种写法,学到了!
none
2023-02-14 20:27:10 +08:00
@Nazz
用接口约束的话,那这里泛型好像可以去掉了,直接定义接口类型的参数不是更简单
func handle(a Value) {
fmt.Println(a.GetValue())
}
Nazz
2023-02-14 21:12:52 +08:00
@none 提问者想要的只是编译期类型检查
lesismal
2023-02-14 22:57:00 +08:00
@Nazz
如果入参不是对应具体 interface{...}类型也是编译期就失败的。

@aapeli
个人感受是:go 的泛型适合运算符重载之类的基础类型场景,自定义类型通常用 interface{...}或者切面更加简洁清晰、硬要为了用泛型而去用泛型,可能会让代码更难看。

再根据我浅薄的 go 泛型使用经验(不超过 10 分钟),能用接口、切面解决的问题不要用泛型就是最优解。
——不一定对,仅供各位参考
Nazz
2023-02-14 23:18:39 +08:00
@lesismal 泛型主要是用来实现数据结构与算法的. 有时候也需要在泛型方法里面转成 interface{}再断言
GeruzoniAnsasu
2023-02-15 02:37:44 +08:00
这个问题之前讨论过
/t/904511


type typeProxy interface {
Close() error
}

func CanCompile[T T1 | T2](t T) error {
return any(t).(typeProxy).Close()
}

CanCompile(T3{}) // 拒绝编译

用一个接口类型代理 method 访问,并不需要 switch type
lysS
2023-02-15 09:59:08 +08:00
@Nazz 这样子还不如直接用接口

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/916084

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX