How can two different types implement the same method in golang using interfaces?

go
13,004

Solution 1

No you can't, but you could create a base type and then embed it into your 2 struct, therefore only needing an implementation for the base type:

type WithString struct {
    str string
}

type First struct {
    WithString
}

type Second struct {
    WithString
}

type A interface {
    PrintStr() //print First.str or Second.str
}

func (w WithString) PrintStr() {
    fmt.Print(w.str)
}

Usage:

a := First{
    WithString: WithString{
        str: "foo",
    },
}

Complete Example on Playground

Embed documentation

Solution 2

If the printing logic depends on the interface but not on the structs themselves, then it is better to move printing to a free function that operates over an interface.

In your case, the PrintStr method is used to print a string which is a member of each structure.
In this case, it means that each structure should implement an interface that returns the necessary string used for printing, and PrintStr becomes a function taking a Printable parameter.

type First struct {
    str string
}
type Second struct {
    str string
}

type Printable interface {
     String() string
}

func (p First) String() string {
    return p.str
}

func (p Second) String() string {
    return p.str
}

func PrintStr(p Printable) {
    fmt.Print(p.String())
}

Your use of the A interface is non-idiomatic because an interface should not depend on the implementation of its functionality.

Instead, with this solution, you can still keep the A interface, but simplify each implementation:

func (f First) PrintStr() {
    PrintStr(f)
}

func (s Second) PrintStr() {
    PrintStr(s)
}

It is still redundant, but the logic lies in the function that is called from there, limiting the need to do copy-pasting in case of modification of the printing logic.

This pattern is common in the Go standard library, because many useful functions are built upon interfaces which they cannot extend, for example io.Reader.
It is a simple interface with only one method, but it is used thoroughly from many other packages.
If you look at the ioutil.ReadAll function, it could be argued that it could have been implemented as another method of the io.Reader interface, however this keeps readers simpler, concentrating on their single method, while allowing any implementor to use ReadAll for free.

Solution 3

Maybe not the best way to solve your problem, but you could use a wrapper in order to avoid "implementing" the function twice, something like this:

type First struct {
    str StringWrapper
}
type Second struct {
    str StringWrapper
}


type StringWrapper struct {
    str string
}
func (f StringWrapper) PrintStr() {
    fmt.Print(f.str)
}

func main() {
    var a First = First{str:StringWrapper{str: "aaa"}};
    a.str.PrintStr();
}
Share:
13,004

Related videos on Youtube

Ekaterina
Author by

Ekaterina

Updated on September 15, 2022

Comments

  • Ekaterina
    Ekaterina about 1 year

    Say I have two structs:

    type First struct {
        str string
    }
    type Second struct {
        str string
    }
    

    And I want both of them to implement interface A:

    type A interface {
        PrintStr() //print First.str or Second.str
    }
    

    It seems redundant to have an implementation for both First and Second structs like so:

    func (f First) PrintStr() {
        fmt.Print(f.str)
    }
    
    func (s Second) PrintStr() {
        fmt.Print(s.str)
    }
    

    Is there a way I can have one implementation for all the structs implementing interface A? Something like this, but it doesn't seem to work:

    func (a A) PrintStr() {
        fmt.Print(a.str)
    }
    

    Thank you!

    • TheHippo
      TheHippo over 8 years
      It also seems a little bit redundant to have 2 structs with the same types.
    • Ekaterina
      Ekaterina over 8 years
      Yes, but this is a toy example. First and Second could share some fields and not others. The point is that I want a function to behave in the exact same way for two different types, without having repeated code.
  • Joey
    Joey over 8 years
    what is this sorcery where you define a struct with a non-variable member?! from your answer: type First struct { WithString }... is there any documentation on this? defining a type struct as another type?
  • SirDarius
    SirDarius over 8 years
    @Joey this is called embedding and it is a well known technique, see golang.org/doc/effective_go.html#embedding
  • Arun Gopalpuri
    Arun Gopalpuri over 3 years
    @TheHippo - what's the point of that interface in your example?
  • Admin
    Admin over 3 years
    I agree with Arun. See the answer by @mihaic.
  • Admin
    Admin over 3 years
    This answer is the same as @TheHippo. See play.golang.org/p/yyRG9B_K7lk. Except no interface.
  • izaban
    izaban over 3 years
    This is wrong, you will get the following error when trying to define the PrintStr function a.str undefined (type A is interface with no methods)
  • Nathaniel Jones
    Nathaniel Jones almost 2 years
    @SirDarius Thanks for that link. I've had a hard time finding examples of embedding with structs, and this tells me without a doubt that it's possible. I need to read Effective Go in its entirety.
  • Nathaniel Jones
    Nathaniel Jones almost 2 years
    Here's an example of embedding in action: github.com/gonum/gonum/blob/v0.9.3/mat/matrix.go#L36