Reputation: 59674
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
?
Upvotes: 119
Views: 44223
Reputation: 24974
In short: It just add an field whose name & type are the same as the interface, and it default to nil.
Attention:
Suggestion:
package struct_learn
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
// refer: https://stackoverflow.com/questions/24537443
type IFoo interface {
Foo()
}
type FooA struct {
IFoo
}
type FooB struct {
IFoo
}
func (b FooB) Foo() {
fmt.Printf("foo() impl in Foo%s\n", "B")
}
func TestStructEmbedInterface_noImplPanic(t *testing.T) {
var a FooA
assert.Panics(t, func() {
a.Foo()
})
}
func TestStructEmbedInterface_impl(t *testing.T) {
var b FooB
assert.NotPanics(t, func() {
b.Foo() // no panic, due to has impl,
})
}
type IFoo2 interface {
IFoo // extend another interface,
Foo2()
}
type FooC struct {
IFoo
IFoo2
}
func TestStructEmbedInterface_noImplPanic_multi(t *testing.T) {
var c FooC
assert.Panics(t, func() {
c.IFoo.Foo()
})
assert.Panics(t, func() {
c.IFoo2.Foo()
})
assert.Panics(t, func() {
c.Foo2()
})
}
func TestStructEmbedInterface_noImplPanic_initIFooOnly(t *testing.T) {
c := &FooC{
IFoo: &FooB{},
}
assert.NotPanics(t, func() {
c.IFoo.Foo() // good
})
assert.Panics(t, func() {
c.IFoo2.Foo() // panic, due to nil,
})
assert.Panics(t, func() {
c.Foo2()
})
}
type FooD struct {
IFoo
IFoo2
}
func (b FooD) Foo() {
fmt.Printf("foo() impl in Foo%s\n", "D")
}
func (b FooD) Foo2() {
fmt.Printf("foo2() impl in Foo%s\n", "D")
}
func TestStructEmbedInterface_noImplPanic_initIFoo2Only(t *testing.T) {
d := &FooD{
IFoo2: &FooD{},
}
assert.Panics(t, func() {
d.IFoo.Foo() // panic, due to nil,
})
assert.NotPanics(t, func() {
d.IFoo2.Foo() // good
})
assert.NotPanics(t, func() {
d.IFoo2.Foo2() // good
d.Foo2() // same as above,
})
}
Output:
=== RUN TestStructEmbedInterface_noImplPanic
--- PASS: TestStructEmbedInterface_noImplPanic (0.00s)
=== RUN TestStructEmbedInterface_impl
foo() impl in FooB
--- PASS: TestStructEmbedInterface_impl (0.00s)
=== RUN TestStructEmbedInterface_noImplPanic_multi
--- PASS: TestStructEmbedInterface_noImplPanic_multi (0.00s)
=== RUN TestStructEmbedInterface_noImplPanic_initIFooOnly
foo() impl in FooB
--- PASS: TestStructEmbedInterface_noImplPanic_initIFooOnly (0.00s)
=== RUN TestStructEmbedInterface_noImplPanic_initIFoo2Only
foo() impl in FooD
foo2() impl in FooD
foo2() impl in FooD
--- PASS: TestStructEmbedInterface_noImplPanic_initIFoo2Only (0.00s)
PASS
Process finished with the exit code 0
Upvotes: 0
Reputation: 24593
Embedding interfaces in a struct allows for partially "overriding" methods from the embedded interfaces. This, in turn, allows for delegation from the embedding struct to the embedded interface implementation.
The following example is taken from this blog post.
Suppose we want to have a socket connection with some additional functionality, like counting the total number of bytes read from it. We can define the following struct:
type StatsConn struct {
net.Conn
BytesRead uint64
}
StatsConn
now implements the net.Conn
interface and can be used anywhere a net.Conn
is expected. When a StatsConn
is initialized with a proper value implementing net.Conn
for the embedded field, it "inherits" all the methods of that value; the key insight is, though, that we can intercept any method we wish, leaving all the others intact. For our purpose in this example, we'd like to intercept the Read
method and record the number of bytes read:
func (sc *StatsConn) Read(p []byte) (int, error) {
n, err := sc.Conn.Read(p)
sc.BytesRead += uint64(n)
return n, err
}
To users of StatsConn
, this change is transparent; we can still call Read
on it and it will do what we expect (due to delegating to sc.Conn.Read
), but it will also do additional bookkeeping.
It's critical to initialize a StatsConn
properly, otherwise the field retains its default value nil
causing a runtime error: invalid memory address or nil pointer dereference
; for example:
conn, err := net.Dial("tcp", u.Host+":80")
if err != nil {
log.Fatal(err)
}
sconn := &StatsConn{conn, 0}
Here net.Dial
returns a value that implements net.Conn
, so we can use that to initialize the embedded field of StatsConn
.
We can now pass our sconn
to any function that expects a net.Conn
argument, e.g:
resp, err := ioutil.ReadAll(sconn)
if err != nil {
log.Fatal(err)
And later we can access its BytesRead
field to get the total.
This is an example of wrapping an interface. We created a new type that implements an existing interface, but reused an embedded value to implement most of the functionality. We could implement this without embedding by having an explicit conn
field like this:
type StatsConn struct {
conn net.Conn
BytesRead uint64
}
And then writing forwarding methods for each method in the net.Conn
interface, e.g.:
func (sc *StatsConn) Close() error {
return sc.conn.Close()
}
However, the net.Conn
interface has many methods. Writing forwarding methods for all of them is tedious and unnecessary. Embedding the interface gives us all these forwarding methods for free, and we can override just the ones we need.
Upvotes: 6
Reputation: 319
I will try another, low level approach to this. Given the reverse struct:
type reverse struct {
Interface
}
This beside others means, that reverse struct has a field reverse.Interface
, and as a struct fields, it can be nil or have value of type Interface.
If it is not nil, then the fields from the Interface are promoted to the "root" = reverse struct. It might be eclipsed by fields defined directly on the reverse struct, but that is not our case.
When You do something like: foo := reverse{}, you can println it via fmt.Printf("%+v", foo) and got
{Interface:<nil>}
When you do the
foo := reverse{someInterfaceInstance}
It is equivalent of:
foo := reverse{Interface: someInterfaceInstance}
It feels to me like You declare expectation, that implementation of Interface API should by injected into your struct reverse in runtime. And this api will be then promoted to the root of struct reverse. At the same time, this still allow inconsistency, where You have reverse struct instance with reverse.Interface = < Nil>, You compile it and get the panic on runtime.
When we look back to the specifically example of the reverse in OP, I can see it as a pattern, how you can replace/extend behaviour of some instance / implementation kind of in runtime contrary to working with types more like in compile time when You do embedding of structs instead of interfaces.
Still, it confuses me a lot. Especially the state where the Interface is Nil :(.
Upvotes: 0
Reputation: 14280
I find this feature very helpful when writing mocks in tests.
Here is such an example:
package main_test
import (
"fmt"
"testing"
)
// Item represents the entity retrieved from the store
// It's not relevant in this example
type Item struct {
First, Last string
}
// Store abstracts the DB store
type Store interface {
Create(string, string) (*Item, error)
GetByID(string) (*Item, error)
Update(*Item) error
HealthCheck() error
Close() error
}
// this is a mock implementing Store interface
type storeMock struct {
Store
// healthy is false by default
healthy bool
}
// HealthCheck is mocked function
func (s *storeMock) HealthCheck() error {
if !s.healthy {
return fmt.Errorf("mock error")
}
return nil
}
// IsHealthy is the tested function
func IsHealthy(s Store) bool {
return s.HealthCheck() == nil
}
func TestIsHealthy(t *testing.T) {
mock := &storeMock{}
if IsHealthy(mock) {
t.Errorf("IsHealthy should return false")
}
mock = &storeMock{healthy: true}
if !IsHealthy(mock) {
t.Errorf("IsHealthy should return true")
}
}
By using:
type storeMock struct {
Store
...
}
One doesn't need to mock all Store
methods. Only HealthCheck
can be mocked, since only this method is used in the TestIsHealthy
test.
Below the result of the test
command:
$ go test -run '^TestIsHealthy$' ./main_test.go
ok command-line-arguments 0.003s
A real world example of this use case one can find when testing the AWS SDK.
To make it even more obvious, here is the ugly alternative - the minimum one needs to implement to satisfy the Store
interface:
type storeMock struct {
healthy bool
}
func (s *storeMock) Create(a, b string) (i *Item, err error) {
return
}
func (s *storeMock) GetByID(a string) (i *Item, err error) {
return
}
func (s *storeMock) Update(i *Item) (err error) {
return
}
// HealthCheck is mocked function
func (s *storeMock) HealthCheck() error {
if !s.healthy {
return fmt.Errorf("mock error")
}
return nil
}
func (s *storeMock) Close() (err error) {
return
}
Upvotes: 5
Reputation: 11541
I will give my explanation too. The sort
package defines an unexported type reverse
, which is a struct, that embeds Interface
.
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
This permits Reverse to use the methods of another Interface implementation. This is the so called composition
, which is a powerful feature of Go.
The Less
method for reverse
calls the Less
method of the embedded Interface
value, but with the indices flipped, reversing the order of the sort results.
// 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)
}
Len
and Swap
the other two methods of reverse
, are implicitly provided by the original Interface
value because it is an embedded field. The exported Reverse
function returns an instance of the reverse
type that contains the original Interface
value.
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
Upvotes: 9
Reputation: 59674
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"})
}
Upvotes: 56
Reputation: 48330
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.
Reverse
method to the sort.Interface
?Any of this change would require many many more lines of code across thousands of packages that want to use the standard reverse functionality.
Upvotes: 96
Reputation: 57739
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.
Upvotes: 30