Go - 在列表中存储具有相同嵌入结构的结构

Go - storing structs with the same embedded struct in a list

我有多个具有相同嵌入式结构的结构。在其中一个结构中,我想存储嵌入基本结构的任何结构的列表。这是一个显示案例的小片段。

package main

type Symbol interface{}

type Base struct {
    a int32
}

type Foo struct {
    Base
    b int32
    Symbols []Base
    // Below works
    // Symbols []Symbol
}

type Bar struct {
    Base
    c int32
}

func main () {
    var bar Bar
    var foo Foo
    foo.Symbols = append(foo.Symbols, bar)
}

但是,这不起作用。我得到 ./main.go:25:22: cannot use bar (type Bar) as type Base in append。当我使用空 Symbol 界面时,一切正常。然而,这种方法完全绕过了类型系统,因为现在所有内容都可以存储在列表中。我想以某种方式表示列表中只能存储 BaseFooBar,以便编译器可以检查是否满足此要求。我的结构没有任何方法,当然没有共享行为并可以添加到接口的方法。向接口和虚拟实现添加一些虚拟方法似乎非常人为。处理此类场景的 Go 惯用方式是什么?

重要的是相同的界面:

package main

import (
    "fmt"
)

type Item interface{
    fmt.Stringer
}

type ItemA struct {
}

func (a ItemA) String() string {
    return "item A"
}

type ItemB struct {
}

func (a ItemB) String() string {
    return "item B"
}

func main() {
    items := make([]Item, 0)
    items = append(items, ItemA{}, ItemB{})
    fmt.Printf("Items: %v", items)
}

您似乎期待的是 subtype polymorphism。 Go 不支持非接口类型的动态绑定。因此你可以使用 interfaces

package main

func main(){
    var bar Bar
    var foo Foo
    foo.Symbols = append(foo.Symbols, bar)
}

type Symbol interface {
    Symbol()
}

type Base struct {
    a int32
}

func (b Base)Symbol(){}

type Foo struct {
    Base
    b int32
    Symbols []Symbol
}

type Bar struct {
    Base
    c int32
}

或者如果你不喜欢使用接口,你可以使用像下面这样的技巧来反射。

package main

import "fmt"

func main(){
    var bar Bar
    var foo Foo
    err := foo.AddSymbol(bar)
    if err != nil{
        fmt.Println(err)
    }
}

type Base struct {
    a int32
}

func (b Base)Symbol(){}

type Foo struct {
    Base
    b int32
    symbol []interface{} // field made private
}

// AddSymbol : helper to append values
func (f *Foo)AddSymbol(in interface{})(err error){
    if f.symbol == nil{
        f.symbol = make([]interface{},0)
    }

    switch typ := in.(type) {
    case Base,Bar,Foo:
        f.symbol = append(f.symbol,in)
    default:
        return fmt.Errorf("provided type: %s is not supported",typ)
    }

    return nil
}

type Bar struct {
    Base
    c int32
}

我做了一些搜索和阅读。我想要实现的目标需要所谓的“总和类型”。目前 Go 不支持求和类型。但是,几乎没有其他方法可以模拟求和类型的行为。此处对它们进行了很好的描述 Alternatives to sum types in Go.

此外,看起来 Go 2 可能会支持求和类型。进一步阅读 proposal: spec: add sum types / discriminated unions, spec: generics: use type sets to remove type keyword in constraints