DEV Community

Vicent
Vicent

Posted on

Polymorphism for Go slices

Hi devs!

Today I'm going to show you how to implement polymorphism with slices on golang.

Imagine the situation where you want to implement a similar function on different structs. Interfaces are your best friend! For example, the classic function toString()

//our interface
type Element interface {
    toString() string
}

//our first struct
type MyCustomStruct1 struct {
    prop1 string
    prop2 string
}

//our second struct
type MyCustomStruct2 struct {
    awesomeProp string
}

//abstract implementation of our interface for MyCustomStruct1
func (m MyCustomStruct1) toString() string {
    return m.prop1 + m.prop2
}

//abstract implementation of our interface for MyCustomStruct2
func (m MyCustomStruct2) toString() string {
    return m.awesomeProp
}

Enter fullscreen mode Exit fullscreen mode

At this point, nothing new of interfaces functionallity in golang. Well, now, we could think we can code something like that:


func printElementList(elements []Element) {
    for _, e := range elements {
        fmt.Println(e.toString())
    }

}

//code inside main or another function
ms1 := []MyCustomStruct1{
    MyCustomStruct1{prop1: "foo", prop2:"bar"},
    MyCustomStruct1{prop1: "foo1", prop2:"bar1"},
}


printElementList(ms1)

Enter fullscreen mode Exit fullscreen mode

That may work in other languages thinking on common functionality of hierarchy and polymorphism. However, the compiler will throw us that error:

cannot use ms1 (type []MyCustomStruct1) as type []Element in argument to printElementList

Ok, how can we aboard that problem? At the beggining I thought a map[int]Element could solve the problem, but I don't want to have my slice ordered on a map where I'm not going to use integers.

Well, which is my solution? I surrounded the elements of slice with interfaces just like that:

var elements []interface{Element}

for _, m:= range ms1 {
    elements = append(elements, m)
}

Enter fullscreen mode Exit fullscreen mode

And, of course, I changed the declaration of function and the call:

func printElementList(elements []interface{Element}) {
    for _, e := range elements {
        fmt.Println(e.toString())
    }
}

//code inside main or another function
printElementList(elements)


Enter fullscreen mode Exit fullscreen mode

At this point, we can use printElementList function with an slice of MyCustomStruct2 or another struct that implements Element interface:

ms2 := []MyCustomStruct2{
     MyCustomStruct2{"foo"},
     MyCustomStruct2{"bar"},
}

var elements2 []interface{Element}

for _, m:= range ms2 {
    elements2 = append(elements2, m)
}

printElementList(elements2)
Enter fullscreen mode Exit fullscreen mode

What do you think of that? Is an over complicated implementation?

Here you have the link to full code on play.golang:

https://play.golang.org/p/B88iV16nm4u

Top comments (2)

Collapse
 
peppy profile image
Peppy Sisay

Creative approach!

Another way is to type the slices as a list of Elements.

elements := []Element{
    MyCustomStruct1{prop1: "foo", prop2:"bar"},
    MyCustomStruct1{prop1: "foo1", prop2:"bar1"},
}
elements2 := []Element{
    MyCustomStruct2{"foo"},
    MyCustomStruct2{"bar"},
}
Enter fullscreen mode Exit fullscreen mode
func printElementList(elements []Element) {
    for _, e := range elements {
        fmt.Println(e.toString())
    }
}
Enter fullscreen mode Exit fullscreen mode

play.golang.org/p/qdbRJ1Kq_Kq

Collapse
 
vicentdev profile image
Vicent

Wow, yes, I think I tried to use polymorphism on the wrong way. The problem I see with that implementation is the elements in the slice can be another type that implements the same interface.