Home > Back-end >  Go - storing structs with the same embedded struct in a list
Go - storing structs with the same embedded struct in a list

Time:10-04

I have multiple structs with the same embedded struct. In one of the structs I would like to store a list of any struct that embeds the base struct. Here is a small snippet showing the case.

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)
}

However, this does not work. I get ./main.go:25:22: cannot use bar (type Bar) as type Base in append. When I use the empty Symbol interface, everything works. However this approach completely bypasses the type system as now everything can be stored in the list. I would like to somehow denote that only Base, Foo and Bar can be stored in the list, so that compiler can check if this rquirement is met. My structs do not have any methods, for sure not the ones that share behavior and can be added to the interface. Adding some dummy method to the interface and dummy implementations seems very artificial. What is the Go idiomatic way of handling such scenarios?

CodePudding user response:

What matters is the same interface:

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)
}

CodePudding user response:

What you seem to be expecting is subtype polymorphism. Go does not support dynamic binding for non-interface types. Therefore you you could either use 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
}

or if you dislike using interfaces, you can use a trick like below with reflect.

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
}
  • Related