V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
blue7wings
V2EX  ›  Go 编程语言

Golang 如何使用 struct 泛型?

  •  
  •   blue7wings · 2022-02-21 18:12:26 +08:00 · 6153 次点击
    这是一个创建于 997 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码:

    
    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 的泛型,还不是特别理解,网上也没搜到相关问题,请教一下大家,这里是哪里的错误?

    19 条回复    2022-02-22 20:12:10 +08:00
    imkerberos
        1
    imkerberos  
       2022-02-21 18:34:32 +08:00   ❤️ 1
    大道至简
    proxytoworld
        2
    proxytoworld  
       2022-02-21 18:45:36 +08:00
    楼主代码确定没问题吗,为什么我报错了
    GM
        3
    GM  
       2022-02-21 18:46:25 +08:00
    大道至简 /go 头
    lesismal
        4
    lesismal  
       2022-02-21 18:58:12 +08:00
    interface 相当于虚基类,配合切面使用基本就能达到其他语言 class 的效果:

    gist.github.com/lesismal/e2203edd06a17fa5043bbfdbf6cbbaf7
    janxin
        5
    janxin  
       2022-02-21 19:13:27 +08:00
    因为这里泛型不能这么用...

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

    type B struct {
    AID string
    BID string
    }

    都是不行的... 原因是 A 不满足 BID 约束
    dcalsky
        6
    dcalsky  
       2022-02-21 20:04:42 +08:00
    @janxin
    type A struct {
    AID string
    BID string
    }

    type B struct {
    AID string
    BID string
    }

    这样也是不行的
    thevita
        7
    thevita  
       2022-02-21 20:10:11 +08:00
    @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
        8
    eastphoton  
       2022-02-21 20:24:12 +08:00
    你想用的是多态吧。。。
    eastphoton
        9
    eastphoton  
       2022-02-21 20:40:39 +08:00
    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
        10
    ZSeptember  
       2022-02-21 21:04:07 +08:00
    union 不支持 struct ,只支持基本类型
    thevita
        11
    thevita  
       2022-02-21 21:20:11 +08:00
    @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
        12
    janxin  
       2022-02-21 21:33:25 +08:00
    @thevita 因为你这个地方的约束是用的 fmt.Stringer

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

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

    return val{
    AID: "AID",
    }
    janxin
        13
    janxin  
       2022-02-21 21:35:13 +08:00
    @janxin 具体应该不是这个约束...记不太清具体是那个了
    thevita
        14
    thevita  
       2022-02-21 21:46:06 +08:00
    @janxin

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

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

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

    [![HvvkV0.jpg]( https://s4.ax1x.com/2022/02/21/HvvkV0.jpg)]( https://imgtu.com/i/HvvkV0)
    janxin
        15
    janxin  
       2022-02-21 21:51:23 +08:00
    @thevita 你这是编译完成后展开之后的情况,编译成功之后就会被展开。我说的是编译期间约束检查。
    janxin
        16
    janxin  
       2022-02-21 22:01:29 +08:00
    @thevita 换句话说,对于#6 中的例子中,你使用 fmt.Println(v.AID)是可以编译成功的,但是对顶楼和#7 例子结合的情况,编译是无法通过的。
    lysS
        17
    lysS  
       2022-02-22 11:19:14 +08:00
    go 的泛型还没发布吧,要拉对应分支自己编译 go
    macscsbf
        18
    macscsbf  
       2022-02-22 13:20:04 +08:00
    go 官网提供的泛型教程感觉就不太行
    https://taoshu.in/go/generics/design.html?utm_source=wechat_session&utm_medium=social&utm_oi=643495174752309248

    不知道这个能不能解决你的问题
    cmdOptionKana
        19
    cmdOptionKana  
       2022-02-22 20:12:10 +08:00
    刚刚看到一篇文章 《 Go 泛型简明入门教程》,感觉应该这样写:(我没验证)

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

    func Get[val A | B]() val {
    return A{
    AID: "AID",
    }
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2574 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 16:02 · PVG 00:02 · LAX 08:02 · JFK 11:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.