Search code examples
gostructgo-interface

Chainable struct methods that satisfies multiple interfaces?


I have a need to create multiple structs with almost the same fields and methods with the same actions. Instead of doing that I thought, why not create a single struct and use interfaces to limit interactions. It worked great! Now the problem. I want the methods to be chainable. To do that, methods need to return a reference to the struct. After doing that, all the returns complain that (struct) does not implement <interface> (wrong type for <method> method). Which is expected.

The question is, is it possible to use a single struct with multiple interfaces that has chainable methods? Or is creating individual, duplicate, structs for every interface the only way?

I thought of using composition but I still need to define methods that will call the embedded struct methods, in which case there's no difference to creating new pure structs.

Example problem:

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

package main

import (
    "fmt"
)

type A interface {
    SetA(string) A
    Done()
}

type B interface {
    SetB(string) B
    Done()
}

type t struct {
    a string
    b string
}

func (t *t) SetA(a string) *t { t.a = a; return t }
func (t *t) SetB(b string) *t { t.b = b; return t }
func (t *t) Done()            { fmt.Println(t.a, t.b) }

func NewTA() A {
    return &t{}
}

func NewTB() B {
    return &t{}
}

func main() {
    fmt.Println("Hello, playground")

    ta := NewTA()
    ta.SetA("a")
    ta.Done()

    tb := NewTB()
    tb.SetB("b")
    tb.Done()
}


Solution

  • When you use *t as return type in SetA and SetB that means t are not implement A and B interface. The signature of SetA and SetB function of *t doesn't match with interface A and B accordingly.

    Accually SetA(a string) A and SetA(a string) *t are not same think. You used A as return type for SetA in interface but use *t as return type for t, go doesn't support this. Same for SetB function

    If you do like this then it will work because now function signature matched

    func (t *t) SetA(a string) A { t.a = a; return A(t) }
    func (t *t) SetB(b string) B { t.b = b; return B(t) }
    

    Code in go playground here