Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meaning of a struct with embedded anonymous interface?

Tags:

go

sort package:

type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

...

type reverse struct {
    Interface
}

What is the meaning of anonymous interface Interface in struct reverse?

like image 595
warvariuc Avatar asked Jul 02 '14 17:07

warvariuc


People also ask

Is a struct an interface?

Structs and interfaces are Go's way of organizing methods and data handling. Where structs define the fields of an object, like a Person's first and last name. The interfaces define the methods; e.g. formatting and returning a Person's full name.

What is the use of struct in Golang?

A structure or struct in Golang is a user-defined type that allows to group/combine items of possibly different types into a single type. Any real-world entity which has some set of properties/fields can be represented as a struct. This concept is generally compared with the classes in object-oriented programming.

Can struct implement interface Golang?

In Go, we can automatically infer that a struct (object) implements an interface when it implements all its methods.

What is interface {} Golang?

interface{} is the Go empty interface, a key concept. Every type implements it by definition. An interface is a type, so you can define for example: type Dog struct { Age interface{} }


3 Answers

In this way reverse implements the sort.Interface and we can override a specific method without having to define all the others

type reverse struct {
        // This embedded Interface permits Reverse to use the methods of
        // another Interface implementation.
        Interface
}

Notice how here it swaps (j,i) instead of (i,j) and also this is the only method declared for the struct reverse even if reverse implement sort.Interface

// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
        return r.Interface.Less(j, i)
}

Whatever struct is passed inside this method we convert it to a new reverse struct.

// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
        return &reverse{data}
}

The real value comes if you think what would you have to do if this approach was not possible.

  1. Add another Reverse method to the sort.Interface ?
  2. Create another ReverseInterface ?
  3. ... ?

Any of this change would require many many more lines of code across thousands of packages that want to use the standard reverse functionality.

like image 90
fabrizioM Avatar answered Oct 17 '22 03:10

fabrizioM


Ok, the accepted answer helped me understand, but I decided to post an explanation which I think suits better my way of thinking.

The "Effective Go" has example of interfaces having embedded other interfaces:

// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
    Reader
    Writer
}

and a struct having embedded other structs:

// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
    *Reader  // *bufio.Reader
    *Writer  // *bufio.Writer
}

But there is no mention of a struct having embedded an interface. I was confused seeing this in sort package:

type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

...

type reverse struct {
    Interface
}

But the idea is simple. It's almost the same as:

type reverse struct {
    IntSlice  // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order
}

methods of IntSlice being promoted to reverse.

And this:

type reverse struct {
    Interface
}

means that sort.reverse can embed any struct that implements interface sort.Interface and whatever methods that interface has, they will be promoted to reverse.

sort.Interface has method Less(i, j int) bool which now can be overridden:

// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
    return r.Interface.Less(j, i)
}

My confusion in understanding

type reverse struct {
    Interface
}

was that I thought that a struct always has fixed structure, i.e. fixed number of fields of fixed types.

But the following proves me wrong:

package main

import "fmt"

// some interface
type Stringer interface {
    String() string
}

// a struct that implements Stringer interface
type Struct1 struct {
    field1 string
}

func (s Struct1) String() string {
    return s.field1
}


// another struct that implements Stringer interface, but has a different set of fields
type Struct2 struct {
    field1 []string
    dummy bool
}

func (s Struct2) String() string {
    return fmt.Sprintf("%v, %v", s.field1, s.dummy)
}


// container that can embedd any struct which implements Stringer interface
type StringerContainer struct {
    Stringer
}


func main() {
    // the following prints: This is Struct1
    fmt.Println(StringerContainer{Struct1{"This is Struct1"}})
    // the following prints: [This is Struct1], true
    fmt.Println(StringerContainer{Struct2{[]string{"This", "is", "Struct1"}, true}})
    // the following does not compile:
    // cannot use "This is a type that does not implement Stringer" (type string)
    // as type Stringer in field value:
    // string does not implement Stringer (missing String method)
    fmt.Println(StringerContainer{"This is a type that does not implement Stringer"})
}
like image 48
warvariuc Avatar answered Oct 17 '22 05:10

warvariuc


The statement

type reverse struct {
    Interface
}

enables you to initialize reverse with everything that implements the interface Interface. Example:

&reverse{sort.Intslice([]int{1,2,3})}

This way, all methods implemented by the embedded Interface value get populated to the outside while you are still able to override some of them in reverse, for example Less to reverse the sorting.

This is what actually happens when you use sort.Reverse. You can read about embedding in the struct section of the spec.

like image 30
nemo Avatar answered Oct 17 '22 03:10

nemo