88

In Golang, we use structs with receiver methods. everything is perfect up to here.
I'm not sure what interfaces are, however. We define methods in structs and if we want to implement a method on a struct, we write it anyway again under another struct.
This means that interfaces seem to be just method definitions, taking just extra unneeded space on our page.

Is there any example explaining why I need an interface?

nikoss
  • 3,254
  • 2
  • 26
  • 40
  • how will you unmarshall unknown structure JSON? or how will fmt.Printf work if it is not there? – YOU Aug 23 '16 at 05:46
  • well it shoudnt work if its not there i guess what do you mean how its gonna work ? it is exported from fmt – nikoss Aug 23 '16 at 06:09
  • Possible duplicate of [Go: What's the meaning of interface{}?](http://stackoverflow.com/questions/23148812/go-whats-the-meaning-of-interface) – molivier Aug 23 '16 at 07:38
  • @molivier this is not about what structs are this is about what they are used for they seems just entirely useless – nikoss Aug 23 '16 at 07:43
  • Look at all the instances of io.Reader and io.Writer in the standard library for a good example. – JimB Aug 23 '16 at 10:40
  • 1
    Why are interfaces needed? Decouple code. See https://stackoverflow.com/a/62297796/12817546. Call a method “dynamically”. See https://stackoverflow.com/a/62336440/12817546. Access a Go package. See https://stackoverflow.com/a/62278078/12817546. Assign any value to a variable. See https://stackoverflow.com/a/62337836/12817546. –  Jul 10 '20 at 09:56

7 Answers7

150

Interfaces are too big of a topic to give an all-depth answer here, but some things to make their use clear.

Interfaces are a tool. Whether you use them or not is up to you, but they can make code clearer, shorter, more readable, and they can provide a nice API between packages, or clients (users) and servers (providers).

Yes, you can create your own struct type, and you can "attach" methods to it, for example:

type Cat struct{}

func (c Cat) Say() string { return "meow" }

type Dog struct{}

func (d Dog) Say() string { return "woof" }

func main() {
    c := Cat{}
    fmt.Println("Cat says:", c.Say())
    d := Dog{}
    fmt.Println("Dog says:", d.Say())
}

We can already see some repetition in the code above: when making both Cat and Dog say something. Can we handle both as the same kind of entity, as animal? Not really. Sure we could handle both as interface{}, but if we do so, we can't call their Say() method because a value of type interface{} does not define any methods.

There is some similarity in both of the above types: both have a method Say() with the same signature (parameters and result types). We can capture this with an interface:

type Sayer interface {
    Say() string
}

The interface contains only the signatures of the methods, but not their implementation.

Note that in Go a type implicitly implements an interface if its method set is a superset of the interface. There is no declaration of the intent. What does this mean? Our previous Cat and Dog types already implement this Sayer interface even though this interface definition didn't even exist when we wrote them earlier, and we didn't touch them to mark them or something. They just do.

Interfaces specify behavior. A type that implements an interface means that type has all the methods the interface "prescribes".

Since both implement Sayer, we can handle both as a value of Sayer, they have this in common. See how we can handle both in unity:

animals := []Sayer{c, d}
for _, a := range animals {
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}

(That reflect part is only to get the type name, don't make much of it as of now.)

The important part is that we could handle both Cat and Dog as the same kind (an interface type), and work with them / use them. If you were quickly on to create additional types with a Say() method, they could line up beside Cat and Dog:

type Horse struct{}

func (h Horse) Say() string { return "neigh" }

animals = append(animals, Horse{})
for _, a := range animals {
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}

Let's say you want to write other code that works with these types. A helper function:

func MakeCatTalk(c Cat) {
    fmt.Println("Cat says:", c.Say())
}

Yes, the above function works with Cat and with nothing else. If you'd want something similar, you'd have to write it for each type. Needless to say how bad this is.

Yes, you could write it to take an argument of interface{}, and use type assertion or type switches, which would reduce the number of helper functions, but still looks really ugly.

The solution? Yes, interfaces. Simply declare the function to take a value of an interface type which defines the behavior you want to do with it, and that's all:

func MakeTalk(s Sayer) {
    fmt.Println(reflect.TypeOf(s).Name(), "says:", s.Say())
}

You can call this function with a value of Cat, Dog, Horse or any other type not known until now, that has a Say() method. Cool.

Try these examples on the Go Playground.

colm.anseo
  • 19,337
  • 4
  • 43
  • 52
icza
  • 389,944
  • 63
  • 907
  • 827
  • 2
    i still cant understand why not just to embed animal into horse or cat or dog still this makes no sense – nikoss Aug 23 '16 at 11:38
  • @nikoss Not sure I understand what you mean, please show how would you add them into a slice and iterate over and call `Say()` on each, for example. – icza Aug 23 '16 at 11:41
  • ah thats the point combining different type of structs now i got it though still its kinda complicated cant get it clear in my mind – nikoss Aug 23 '16 at 11:47
  • 3
    Exactly the same there. Kinda understand what interfaces are but at the same time don't understand the usage in scope of real examples. Cat talks and dog woofs examples do not make sense. I am trying to reflect this on some real world - Customers, Sales Orders, Pickup Orders Other Orders context and have no idea do i need to use Interfaces there. – Dzintars Aug 24 '18 at 10:04
  • 20
    @Dzintars My advice is that if you don't see the benefit of using interfaces in your code, then don't create / use interface just yet. When you start repeating code to handle multiple concrete types, then you will see the benefit / gain of using interfaces, you can add them later too. – icza Aug 24 '18 at 14:12
  • @icza First-rate comment about when to use or not use an interface. I upvoted your answer and quoted your comment. See https://stackoverflow.com/a/62297796/12817546. –  Jul 10 '20 at 10:22
18

interface provide some kinds of generics. Think about duck typing.

type Reader interface {
    Read()
}

func callRead(r Reader) {
    r.Read()
}

type A struct {
}

func (_ A) Read() {
}

type B struct {
}

func (_ B) Read() {
}

It's ok to pass struct A, and B to callRead, because both implement Reader interface. But if without interface, we should write two function for A and B.

func callRead(a A){
     a.Read()
}

func callRead2(b B){
     b.Read()
}
SteveLacy
  • 4,150
  • 2
  • 23
  • 30
zzn
  • 2,376
  • 16
  • 30
  • 2
    Great example. In addition – and this is different from C++/Java – with Go interfaces you don't need to create a `func callRead(r Reader)` either. This is quite powerful! – Evan Moran Dec 18 '18 at 03:51
10

As has already been stated, interfaces are a tool. Not all packages will benefit from them, but for certain programming tasks interfaces can be an extremely useful for abstraction and creating package APIs, and particularly for library code or code which may be implemented in more than one way.

Take for example a package which is responsible for drawing some primitive graphics to a screen. We can think of the absolute basic essential requirements of a screen as being able to draw a pixel, clear the screen, refresh the screen contents periodically, as well as get some basic geometric information about the screen such its current dimensions. Hence a 'Screen' interface might look like this;

type Screen interface {
    Dimensions() (w uint32, h uint32)
    Origin() (x uint32, y uint32)
    Clear()
    Refresh()
    Draw(color Color, point Point)
}

Now our program might have several different "graphics drivers" which could be used by our graphics package to fulfill this basic requirement of a Screen. You might be using some native operating system driver, maybe the SDL2 package and maybe something else. And maybe in your program you need to support multiple options for drawing graphics because its dependent upon the OS environment and so on.

So you might then define three structs, each containing the required resources to the underlying screen drawing routines in the operating system / libraries etc;

type SDLDriver struct {
    window *sdl.Window
    renderer *sdl.Renderer
}

type NativeDriver struct {
    someDataField *Whatever
}

type AnotherDriver struct {
    someDataField *Whatever
}

And then you implement in your code the method interface for all three of these structs so that any of these three structs can satisfy the requirements of the Screen interface

func (s SDLDriver) Dimensions() (w uint32, h uint32) {
    // implement Dimensions()
}

func (s SDLDriver) Origin() (x uint32, y uint32) {
    // implement Origin()
}

func (s SDLDriver) Clear() {
    // implement Clear()
}

func (s SDLDriver) Refresh() {
    // implement Refresh()
}

func (s SDLDriver) Draw(color Color, point Point) {
    // implement Draw()
}

...

func (s NativeDriver) Dimensions() (w uint32, h uint32) {
    // implement Dimensions()
}

func (s NativeDriver) Origin() (x uint32, y uint32) {
    // implement Origin()
}

func (s NativeDriver) Clear() {
    // implement Clear()
}

func (s NativeDriver) Refresh() {
    // implement Refresh()
}

func (s NativeDriver) Draw(color Color, point Point) {
    // implement Draw()
}

... and so on

Now, your outside program really shouldn't care WHICH of these drivers you might be using, just so long as it can clear, draw and refresh the screen through a standard interface. This is abstraction. You provide at the package level the absolute minimum that is required for the rest of your program to work. Only code inside graphics needs to know all of the "nitty gritty" of HOW the operations work.

So you might know which screen driver you need to create for the given environment, maybe this is decided at the start of execution based on checking what's available on the users system. You decide that SDL2 is the best option and you create a new SDLGraphics instance;

sdlGraphics, err := graphics.CreateSDLGraphics(0, 0, 800, 600)

But you can now create a type variable of Screen from this;

var screen graphics.Screen = sdlGraphics

And now you have a generic 'Screen' type called 'screen' which implements (assuming you programmed them) the Clear(), Draw(), Refresh(), Origin() and Dimensions() methods. From this point on in your code you can, in total confidence, issue statements such as

screen.Clear()
screen.Refresh()

And so on... The beauty of this is that you have a standard type called 'Screen' which the rest of your program, which really doesn't care about the inner workings of a graphics library, can use without having to think about it. You can pass around 'Screen' to any function etc in confidence that it will just work.

Interfaces are super useful and they really help you think about the function of your code rather than the data in your structs. And small interfaces are better!

For example instead of having a whole bunch of rendering operations inside the Screen interface, maybe you'll design a second interface like this;

type Renderer interface {
    Fill(rect Rect, color Color)
    DrawLine(x float64, y float64, color Color)
    ... and so on
}

It definitely takes some getting used to, depending on your programming experience and which languages you've used before. If you've been a strict python programmer up until now you'll find Go quite different, but if you've been using Java/C++ then you'll figure out Go pretty quickly. Interfaces give you object-oriented-ness without the annoyance that exists in other languages (e.g. Java).

Andy Joiner
  • 109
  • 1
  • 2
5

Where I could see interface being useful is with implementing private struct fields. For example if you have this code:

package main
type Halloween struct {
   Day, Month string
}
func NewHalloween() Halloween {
   return Halloween { Month: "October", Day: "31" }
}
func (o Halloween) UK(Year string) string {
   return o.Day + " " + o.Month + " " + Year
}
func (o Halloween) US(Year string) string {
   return o.Month + " " + o.Day + " " + Year
}
func main() {
   o := NewHalloween()
   s_uk := o.UK("2020")
   s_us := o.US("2020")
   println(s_uk, s_us)
}

Then o has access to all the struct fields. You may not want that. In that case you can use something like this:

type Country interface {
   UK(string) string
   US(string) string
}
func NewHalloween() Country {
   o := Halloween { Month: "October", Day: "31" }
   return Country(o)
}

The only change we made was adding the interface, then returning the struct wrapped in the interface. In this case only the methods will have access to the struct fields.

Zombo
  • 1
  • 62
  • 391
  • 407
2

I will show here, two interesting use cases of interfaces in Go:

1- See these two simple interfaces:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

Using these two simple interfaces you may do this interesting magic:

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "io"
    "os"
    "strings"
)

func main() {
    file, err := os.Create("log.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    w := io.MultiWriter(file, os.Stdout)
    r := strings.NewReader("You'll see this string twice!!\n")
    io.Copy(w, r)

    slice := []byte{33, 34, 35, 36, 37, 38, 39, 10, 13}
    io.Copy(w, bytes.NewReader(slice)) // !"#$%&'

    buf := &bytes.Buffer{}
    io.Copy(buf, bytes.NewReader(slice))
    fmt.Println(buf.Bytes()) // [33 34 35 36 37 38 39 10 13]

    _, err = file.Seek(0, 0)
    if err != nil {
        panic(err)
    }

    r = strings.NewReader("Hello\nWorld\nThis\nis\nVery\nnice\nInterfacing.\n")
    rdr := io.MultiReader(r, file)
    scanner := bufio.NewScanner(rdr)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
}

Output:

You'll see this string twice!!
!"#$%&'

[33 34 35 36 37 38 39 10 13]
Hello
World
This
is
Very
nice
Interfacing.
You'll see this string twice!!
!"#$%&'

I hope this code is clear enough:
reads from string using strings.NewReader and writes concurrently to both file and os.Stdout using io.MultiWriter with just io.Copy(w, r). Then reads from slice using bytes.NewReader(slice) and writes concurrently to both file and os.Stdout. Then copy slice to the buffer io.Copy(buf, bytes.NewReader(slice)) then goto the file origin using file.Seek(0, 0) then first read from string using strings.NewReader then continue reading that file using io.MultiReader(r, file) and bufio.NewScanner and Print all of then using fmt.Println(scanner.Text()).


2- And this is another interesting use of interface:

package main

import "fmt"

func main() {
    i := show()
    fmt.Println(i) // 0

    i = show(1, 2, "AB", 'c', 'd', []int{1, 2, 3}, [...]int{1, 2})
    fmt.Println(i) // 7

}
func show(a ...interface{}) (count int) {
    for _, b := range a {
        if v, ok := b.(int); ok {
            fmt.Println("int: ", v)
        }
    }
    return len(a)
}

output:

0
int:  1
int:  2
7

And nice example to see: Explain Type Assertions in Go

Also see: Go: What's the meaning of interface{}?

Community
  • 1
  • 1
1
  1. if you need a method\s to be implemented regardless of the struct.

    you might have a handler method to access your local structs and use the handler before knowing the struct.

  2. if you need a behavior unique to other or current struct.

    you might want your interface be viewed with few methods because users might never use them. you might want your structs to be divided by its use cases.

  3. if you need a type that implements anything.

    you might know or not the type but at least you have the value.

go je jo
  • 311
  • 4
  • 8
1

What is the interface?

In Go, an interface is a custom type that other types (interfaces) are able to implement. This is one of the main mechanisms for implementing abstraction in Go.

The interface describes a type by its behavior. It is done by describing methods that show what it can do:

Run in the Playground

package main

import "fmt"

// "Can Move" because has Move() method
type Animal interface {
    Move()
}

// Zebra is an "Animal" because can Move()
type Zebra struct {
    Iam string
}

func (z Zebra) Move() {
    fmt.Println("Zebra says: \"Animal\" can only move. I can move too, therefore I am an \"Animal\"!\n")
}

// "Can Move" because implements the "Animal" that can move
// "Can Swim" because has Swim() method
type Fish interface {
    Animal
    Swim()
}

// ClownFish is an "Animal" because can Move()
// ClownFish is a "Fish" because can Move() and Swim()
type ClownFish struct {
    Iam string
}

func (cf ClownFish) Move() {
    fmt.Println("ClownFish says: \"Animal\" can only move. I can move too, therefore I am an \"Animal\"!")
}

func (cf ClownFish) Swim() {
    fmt.Println("ClownFish says: \"Fish\" can move and swim. I can move and swim too, therefore I am a \"Fish\"!\n")
}

// "Can Move" because implements the "Fish" interface which implements an "Animal" interface that can move
// "Can Swim" because implements the "Fish" interface which can swim
// "Can Whistle" because has Whistle() method
type Dolphin interface {
    Fish
    Whistle()
}

// Orca is an "Animal" because can Move()
// Orca is a "Fish" because can Move() and Swim()
// Orca is a "Dolphin" because can Move(), Swim() and Whistle()
type Orca struct {
    Iam string
}

func (o Orca) Move() {
    fmt.Println("Orca says: \"Animal\" can only move. I can move too, therefore I am an \"Animal\"!")
}

func (o Orca) Swim() {
    fmt.Println("Orca says: \"Fish\" can move and swim. I can move and swim too, therefore I am a \"Fish\"!")
}

func (o Orca) Whistle() {
    fmt.Println("Orca says: \"Dolphin\" can move, swim and whistle. I can move, swim and whistle too, therefore I am a \"Dolphin\"!\n")
}

func main() {

    var pico Zebra = Zebra{Iam: "Zebra animal"}
    // pico can...
    pico.Move()

    var nemo ClownFish = ClownFish{Iam: "Clown fish"}
    // nemo can...
    nemo.Move()
    nemo.Swim()

    var luna Orca = Orca{Iam: "Orca dolphin"}
    // luna can...
    luna.Move()
    luna.Swim()
    luna.Whistle()

    // let's make slices with our "custom" types
    var anything []interface{}
    var animals []Animal
    var fishes []Fish
    var dolphins []Dolphin

    // we can add any type in "empty interface" type slice
    anything = append(anything, pico)
    anything = append(anything, nemo)
    anything = append(anything, luna)
    anything = append(anything, 5)
    anything = append(anything, "abcd")
    fmt.Printf("anything: %v\n", anything) // anything: [{Zebra animal} {Clown fish} {Orca dolphin} 5 abcd]

    // only Animal type can go here
    animals = append(animals, pico)
    animals = append(animals, nemo)
    animals = append(animals, luna)
    fmt.Printf("animals: %v\n", animals) // animals: [{Zebra animal} {Clown fish} {Orca dolphin}]

    // only Fish type can go here
    fishes = append(fishes, nemo)
    fishes = append(fishes, luna)
    fmt.Printf("fishes: %v\n", fishes) // fishes: [{Clown fish} {Orca dolphin}]

    // only Dolphin type can go here
    dolphins = append(dolphins, luna)
    // if you try to add a "Fish" to the slice of "Dolphin"s you will get an error:
    // cannot use nemo (variable of type ClownFish) as type Dolphin in argument to append: ClownFish does not implement Dolphin (missing Whistle method)
    // dolphins = append(dolphins, nemo)
    fmt.Printf("dolphins: %v\n", dolphins) // dolphins: [{Orca dolphin}]
}

As you can see, the interface helps us to add Zebra, ClownFish, and Orca types into the []Animal slice. Without the interface, this would've been impossible. In simple words, an interface is a custom type or tool, or whatever you want to call it, that groups other types by their behavior.

interface{}

Empty interface interface{} doesn't specify any method or implement any other interface, which makes it a universal type, because all other types comply with it.

As you can see in this fragment of the above code, we can push any type in the anything slice:

// we can add any type in "empty interface" type slice
anything = append(anything, pico)
anything = append(anything, nemo)
anything = append(anything, luna)
anything = append(anything, 5)
anything = append(anything, "abcd")
fmt.Printf("anything: %v\n", anything) // anything: [{Zebra animal} {Clown fish} {Orca dolphin} 5 abcd]

Mistakes

  1. Adding methods to the interface which make it bulky or context-specific

Run in the Playground

    package main

    import "fmt"

    // isFish() method makes it context-specific
    // when we start using other animal types are we going to use isBird(), isCarnivore(), etc.?
    type Animal interface {
        Move()
        // isFish()
    }

    // Having a new interface or type which implements "Animal" is a
    // way better design because we can create other types as we need them
    type Fish interface {
        Animal
        FishSpecificBehavior()
    }

    type Salmon struct {
        name string
    }

    func (s Salmon) Move()                 {}
    func (s Salmon) FishSpecificBehavior() {}

    func main() {
        var f Fish = Salmon{"Salty"}
        fmt.Printf("fish is a Salmon by the name \"Salty\": %v\n", f) // fish is a salmon by the name "Salty": {Salty}
        fmt.Println("This is an extendable design!")
    }
  1. Use a pointer receiver *T on the interface method, but try to apply the interface on the actual instance T

Run in the Playground

    package main

    import "fmt"

    type I interface {
        a()
    }

    type T struct{}

    // in this case *T implements the interface "I", but not T
    func (t *T) a() {}

    type M struct{}

    // in this case M and *M both implement the interface "I"
    func (t M) a() {}

    func main() {
        var b I = &T{}
        // var b I = T{} // cannot use T{} (value of type T) as type I in variable declaration: T does not implement I (a method has pointer receiver)
        fmt.Printf("b: %v\n", b) // b: &{}

        var c1 I = &M{}
        var c2 I = M{}
        fmt.Printf("c1: %v c2: %v\n", c1, c2) // c1: &{} c2: &{}
    }
  1. Building deeply nested hierarchical interfaces instead of using composition.

Run in the Playground

    package main

    import "fmt"

    // This is a bad design when we mimic inheritance and build a deeply nested structure
    type Animal interface {
        AnimalCan()
    }

    type Fish interface {
        Animal
        FishCan()
    }

    type SwordFish interface {
        Animal
        Fish
        SwordFishCan()
    }

    // This is a good design when we use composition and enforce a flatter structure
    type Movable interface {
        Move()
    }

    type Runnable interface {
        Run()
    }

    type Jumpable interface {
        Jump()
    }

    type Agile interface {
        Movable
        Runnable
        Jumpable
    }

    type Ant struct{}

    func (a Ant) Move() {}

    type Tigre struct{}

    func (t Tigre) Move() {}
    func (t Tigre) Run()  {}
    func (t Tigre) Jump() {}

    func main() {
        var ant Movable = Ant{}
        var tigre Agile = Tigre{}

        fmt.Printf("ant: %v tigre: %v", ant, tiger) // ant: {} tigre: {}
    }
hserge
  • 905
  • 1
  • 9
  • 12