Golang docs provide a clear guidance on how to name single-method interface (by appending "-er"). But what is the best practice to name a multi-method interface that has only a single struct implementing it?
In C# the interface would have "I" prefix, in Java, the class will have "Impl" suffix. Are there similar guidelines for Golang?
The point of having an interface here is to be able to mock it for unit-testing of components that depend on it.
Here is a specific example of UserManager interface. It is going to be consumed by a web api controller that will translate HTTP requests into method calls on this interface. This web api controller will use most of the methods of UserManager.
type UserManager interface { // Should it be UserManagerInterface ?
GetUser(id int) User
CreateUser(user User)
DeleteUser(id int)
ResetPassword(id int, newPassword string)
VerifyEmail(id int)
}
type UserManagerImpl struct { // -Impl, or -Struct, or something else?
}
Coming from Java/C# to Go requires a paradigm shift.
Because they are implemented implicitly, interfaces are defined where they're consumed, not where they're implemented.
Because they're implemented implicitly, smaller interfaces are preferred over larger interfaces, hence the focus on single-method "Verber" interfaces. If you have a function that needs to read bytes, it can take a io.Reader, and the function can be supplied any type that has that method (regardless what other methods it has). You probably don't have a function that calls all 5 of the methods listed in your interface (if so, that function is probably doing too much).
If you feel like naming a struct and an interface the same thing and therefore need some kind of prefix/suffix, you're probably thinking about it the wrong way and might need to reconsider your design.
DBAL is one area where there is sometimes a real case for a larger interface (though it should probably still be composed of smaller interfaces). But in this case, "Impl" doesn't tell you anything - Java and C# love to have pointless interfaces, Go does not. If you'll only ever have one implementation, the interface is probably useless.
If you will have multiple implementations, the differences between them should guide your naming. For example, you might have a PostgresUserManager, a MongoUserManager, and a MockUserManager.
Do you really need UserManagerImpl struct be public? It's kinda common to have a public interface and the corresponding private implementation. Check this and this.
type Store interface {
RunRoot() string
GraphRoot() string
...
}
type store struct {
...
}
func New() Store {
return &store{}
}
func (s *store) RunRoot() string {
return s.runRoot
}
func (s *store) GraphRoot() string {
return s.graphRoot
}
I mean, if you came up with a decent name for the interface, you still can use it for the implementation. But in general, it's good to call things reflecting what they really are regardless of the best practices of the given language. Projects are unique, it's barely possible to make a list of best practices suitable for all the use cases of the language.
The same as #Ivan Velichko I also used to make Interfaces public and struct private like so:
type Service interface {
Do() error
}
type service struct {
dependency dependencies.Dependency
}
func (s *service) Do() error {
return s.depedency.Do()
}
func NewService() Service {
return &service{}
}
However if for some reasons you have to make structs or their properties public and you don't want to write a lot of getters or setters consider examples below:
Make interface name more specific to actions it does
According this articles:
Effective Go
Is there a naming convention for interface + struct pairs?
You should:
By convention, one-method interfaces are named by the method name plus an -er suffix or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.
type ServiceWorker interface {
HandleMessages() error
}
type Service struct {
dependency dependencies.Dependency
}
func (s *service) HandleMessages() error {
return s.depedency.Do()
}
func NewServiceWorker() Service {
return &service{
Property: "property"
}
}
Create function for getting/modifying struct
type Service interface {
Do() error
Service() *service // this will handle getting and modifying your struct
}
type service struct {
dependency dependencies.Dependency
Property string // property to want to expose
}
func (s *service) Do() error {
return s.depedency.Do()
}
func (s *service) Service() *service {
return s
}
func NewService() Service {
return &service{
Property: "property"
}
}
//...
func main() {
s := pkg.NewService()
fmt.Println("p1:", s.Service().Property) // p1: property
s.Service().Property = "second"
fmt.Println("p2:", s.Service().Property) // p2: second
}
Define getters and setters
Define getters and setters in more Java/C# way:
type Service interface {
Do() error
Get() service
Set() *service
}
type service struct {
dependency dependencies.Dependency
Property string // property to want to expose
}
func (s *service) Do() error {
return s.depedency.Do()
}
func (s service) Get() service {
return s
}
func (s *service) Set() *service {
return s
}
func NewService() Service {
return &service{
Property: "property"
}
}
Related
I've just seen Go has incorporated generics in its latest release, and I'm trying to create a small project to understand how it works. I don't seem to figure out how it works apart from very simple functions being now generic. I'd like to be able to do things like this:
type Dao[RT any] interface {
FindOne(id string) *RT
}
type MyDao struct {
}
type ReturnType struct {
id int
}
func (m *MyDao) FindOne(id string) *ReturnType {
panic("implement me")
}
// how should this look like?
func NewMyDao() *Dao[ReturnType] {
return &MyDao[ReturnType]{}
}
Is that even possible? I don't seem to be implementing the interface that way, and I've tried many combinations of the same.
Is there a way to implement a generic interface? If not, is the alternative only to return the interface{} type?
Types don't actually implement generic interfaces, they implement instantiations of generic interfaces. You can't use a generic type (including interfaces) without instantiation. From there, it is just like pre-generics Go, including the difference between methods with pointer receiver.
Therefore it is helpful to think what the methods that use type parameters would look like if you rewrote them with concrete types.
Let's consider a generic interface and some type:
type Getter[T any] interface {
Get() T
}
type MyStruct struct {
Val string
}
There's a few possible cases
Interface with concrete type argument
Instantiate as Getter[string], implemented by types with method Get() string
// implements Getter[string]
func (m MyStruct) Get() string {
return m.Val
}
// ok
func foo() Getter[string] {
return MyStruct{}
}
Interface with type parameter as type argument
Functions that have type parameters may use those to instantiate generic types, e.g. Getter[T]. Implementors must have exactly the Get() T method. For that to be valid, they are also generic and instantiated with the same type parameter:
So this doesn't compile even if T is string
// Getter[T] literally needs implementors with `Get() T` method
func bar[T any]() Getter[T] {
return MyStruct{} // doesn't compile, even if T is string
}
Making MyStruct also parametrized works:
type MyStruct[T any] struct {
Val T
}
func (m MyStruct[T]) Get() T {
return m.Val
}
func bar[T any]() Getter[T] {
return MyStruct[T]{} // ok
}
Concrete interface with generic implementor
Let's reverse the previous cases. We keep the parametrized MyStruct[T any] but now the interface is not parametrized:
type Getter interface {
Get() string
}
In this case, MyStruct implements Getter only when it is instantiated with the necessary concrete type:
// Getter requires method `Get() string`
func baz() Getter {
return MyStruct[string]{} // instantiate with string, ok
// return MyStruct[int]{} // instantiate with something else, doesn't compile
}
Pointer receivers
This follows the same rules as above, but requires instantiating pointer types, as usual:
// pointer receiver, implements Getter[string]
func (m *MyStruct) Get() string {
return m.Val
}
func foo() Getter[string] {
return &MyStruct{} // ok
// return MyStruct{} // doesn't implement
}
and it is the same if MyStruct is generic.
// parametrized pointer receiver
func (m *MyStruct[T]) Get() T {
return m.Val
}
func foo() Getter[string] {
return &MyStruct[string]{} // ok
}
So in your case, the mental exercise of replacing the type params with concrete types gives that Dao[ReturnType] has method FindOne(id string) *ReturnType. The type that implements this method is *MyDao (pointer receiver), therefore:
func NewMyDao() Dao[ReturnType] {
return &MyDao{}
}
The type *MyDao implements the interface Dao[ReturnType]. Thus, the function should look like:
func NewMyDao() Dao[ReturnType] {
return &MyDao{}
}
Note that the return type is an instance of the generic interface, and the returned value is simply an instance of the *MyDao type.
I've just seen Go has incorporated generics in its latest release, and I'm trying to create a small project to understand how it works. I don't seem to figure out how it works apart from very simple functions being now generic. I'd like to be able to do things like this:
type Dao[RT any] interface {
FindOne(id string) *RT
}
type MyDao struct {
}
type ReturnType struct {
id int
}
func (m *MyDao) FindOne(id string) *ReturnType {
panic("implement me")
}
// how should this look like?
func NewMyDao() *Dao[ReturnType] {
return &MyDao[ReturnType]{}
}
Is that even possible? I don't seem to be implementing the interface that way, and I've tried many combinations of the same.
Is there a way to implement a generic interface? If not, is the alternative only to return the interface{} type?
Types don't actually implement generic interfaces, they implement instantiations of generic interfaces. You can't use a generic type (including interfaces) without instantiation. From there, it is just like pre-generics Go, including the difference between methods with pointer receiver.
Therefore it is helpful to think what the methods that use type parameters would look like if you rewrote them with concrete types.
Let's consider a generic interface and some type:
type Getter[T any] interface {
Get() T
}
type MyStruct struct {
Val string
}
There's a few possible cases
Interface with concrete type argument
Instantiate as Getter[string], implemented by types with method Get() string
// implements Getter[string]
func (m MyStruct) Get() string {
return m.Val
}
// ok
func foo() Getter[string] {
return MyStruct{}
}
Interface with type parameter as type argument
Functions that have type parameters may use those to instantiate generic types, e.g. Getter[T]. Implementors must have exactly the Get() T method. For that to be valid, they are also generic and instantiated with the same type parameter:
So this doesn't compile even if T is string
// Getter[T] literally needs implementors with `Get() T` method
func bar[T any]() Getter[T] {
return MyStruct{} // doesn't compile, even if T is string
}
Making MyStruct also parametrized works:
type MyStruct[T any] struct {
Val T
}
func (m MyStruct[T]) Get() T {
return m.Val
}
func bar[T any]() Getter[T] {
return MyStruct[T]{} // ok
}
Concrete interface with generic implementor
Let's reverse the previous cases. We keep the parametrized MyStruct[T any] but now the interface is not parametrized:
type Getter interface {
Get() string
}
In this case, MyStruct implements Getter only when it is instantiated with the necessary concrete type:
// Getter requires method `Get() string`
func baz() Getter {
return MyStruct[string]{} // instantiate with string, ok
// return MyStruct[int]{} // instantiate with something else, doesn't compile
}
Pointer receivers
This follows the same rules as above, but requires instantiating pointer types, as usual:
// pointer receiver, implements Getter[string]
func (m *MyStruct) Get() string {
return m.Val
}
func foo() Getter[string] {
return &MyStruct{} // ok
// return MyStruct{} // doesn't implement
}
and it is the same if MyStruct is generic.
// parametrized pointer receiver
func (m *MyStruct[T]) Get() T {
return m.Val
}
func foo() Getter[string] {
return &MyStruct[string]{} // ok
}
So in your case, the mental exercise of replacing the type params with concrete types gives that Dao[ReturnType] has method FindOne(id string) *ReturnType. The type that implements this method is *MyDao (pointer receiver), therefore:
func NewMyDao() Dao[ReturnType] {
return &MyDao{}
}
The type *MyDao implements the interface Dao[ReturnType]. Thus, the function should look like:
func NewMyDao() Dao[ReturnType] {
return &MyDao{}
}
Note that the return type is an instance of the generic interface, and the returned value is simply an instance of the *MyDao type.
I'm working on a project that would require some level of abstraction on some data and I would like to keep the packages that use the data as independent as possible so I can swap things out in the future.
Here are 2 possible ways that I thought of, one is having a common data interface and have all the places import it. The other is to have each package define its own interfaces and do a type assertion.
Which is the most Go/general way of doing it?
// Importing interface
// src/model
type IData interface {
myint() int
}
// src/dataextractor
import src/model
type DataExtractor struct {
foo() IData
}
// src/service
import src/model
type ServiceDataExtractor interface {
foo() IData
}
type Service struct {
extractor ServiceDataExtractor
}
func (s Service) serve() {
v = s.extractor.foo()
// do stuff with v
}
vs
// type assertion
// src/dataextractor
type DataExtractorData struct{}
func (d DataExtractorData) myint()int {}
type DataExtractor struct {
foo() interface{} {
reutrn DataExtractorData{}
}
}
// src/service
type ServiceData interface {
myint() int
}
type ServiceDataExtractor interface {
foo() interface{}
}
type Service struct {
extractor ServiceDataExtractor
}
func (s Service) serve() {
data := s.extractor.foo()
v, ok := data.(ServiceData)
// do stuff with v
}
Here's the simple rules to keep in mind. An interface type should be declared in the package that wants to receive that interface type. To satisfy an interface, you don't have to import it. All you need to do is declare the methods as are specified by the interface.
When do you use interfaces? Typically, when you have a field, or parameter, that you want to be able to accept multiple data types, and you can do all you need to do with it by calling methods. How do you determine what goes in the interface? You simply ask yourself: "What methods would I need to call on this value to make use of it?"
The only seemingly meaningful method in your example is myint() int, and the only place it seems you intend to use it is in Service.
type Service struct {
extractor interface {
myint() int
}
}
func (s Service) serve() {
s.extractor.myint()
}
I am learning Go at the moment and I write a small project with some probes which report to a internal Log. I have a basic probe and I want create new probes extending the basic probe.
I want save the objects in an array/slice LoadedProbes.
type LoadableProbe struct {
Name string
Probe Probe
Active bool
}
var LoadableProbes []LoadableProbe
The basic probe struct is:
type ProbeModule struct {
version VersionStruct
name string
author string
log []internals.ProbeLog
lastcall time.Time
active bool
}
func (m *ProbeModule) New(name string, jconf JsonConfig) {
// read jsonConfig
}
func (m *ProbeModule) Exec() bool {
// do some stuff
return true
}
func (m *ProbeModule) String() string {
return m.name + " from " + m.author
}
func (m *ProbeModule) GetLogCount() int {
return len(m.log)
}
[...]
I am using this basic struct for other probes, for example:
type ShellProbe struct {
ProbeModule
}
func (s *ShellProbe) New(name string, jconf JsonConfig) {
s.ProbeModule.New(name, jconf)
fmt.Println("Hello from the shell")
}
func (s *ShellProbe) Exec() bool {
// do other stuff
return true
}
during Init() I call the following code:
func init() {
RegisterProbe("ShellProbe", ShellProbe{}, true)
}
func RegisterProbe(name string, probe Probe, state bool) {
LoadableProbes = append(LoadableProbes, LoadableProbe{name, probe, state})
}
The Problem is now that I can't add the type Shellprobe the the LoadableProbe struct, which expects a Probe struct.
My idea was to use interface{} instead the Probe struct in the Loadable Probe struct. But when I call the New() method of the Probe object:
for _, p := range probes.LoadableProbes {
probe.Probe.New(probe.Name, jconf)
}
But I got the error: p.Probe.New undefined (type interface {} is interface with no methods)
how can I solve this problem?
If you will have common data fields in each probe type, you might consider using Probe as a concrete base type defining your base data fields and base methods, and using a new ProbeInterface interface as an abstract base type defining common expected method signatures to allow you to pass around / collect / manage different specialized probe types.
You would embed Probe into each specialized probe type, and methods and fields would be promoted according to rules of embedding. It looks like you're familiar with embedding in general, but the details are worth reviewing in the "Embedding" section of Effective Go if you haven't looked at it recently.
You could override Probe methods in specialized probe types to execute type-specific code.
It might look something like this:
type ProbeInterface interface {
New()
Exec() bool
// whatever other methods are common to all probes
}
type Probe struct {
// whatever's in a probe
}
func (p *Probe) New() {
// init stuff
}
func (p *Probe) Exec() bool {
// exec stuff
return true
}
// Probe's methods and fields are promoted to ShellProbe according to the rules of embedding
type ShellProbe struct {
Probe
// any specialized ShellProbe fields
}
// override Probe's Exec() method to have it do something ShellProbe specific.
func (sp *ShellProbe) Exec() bool {
// do something ShellProbe-ish
return true
}
type LoadableProbe struct {
Name string
P ProbeInterface
Active bool
}
func RegisterProbe(name string, probe ProbeInterface, state bool) {
LoadableProbes = append(LoadableProbes, LoadableProbe{name, probe, state})
}
There are different approaches to your question.
The most direct answer would be: You need to convert your interface{} to a concrete type before calling any methods on it. Example:
probe.Probe.(ShellProbe).New(...)
But this is a really confusing API to use.
A better approach is probably to re-think your entire API. It's hard to do this level of design thinking with the limited information you've provided.
I don't know whether this will work for you, but a common pattern is to define an interface:
type Probe interface {
New(string, JsonConfig)
Exec() bool
// ... etc
}
Then make all of your probe types implement the interface. Then use that interface instead of interface{}, as you initially did:
type LoadableProbe struct {
Name string
Probe Probe
Active bool
}
Then your syntax should work again, because the Probe interface includes a New method.
probe.Probe.New(...)
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?
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.
Add another Reverse method to the sort.Interface ?
Create another ReverseInterface ?
... ?
Any of this change would require many many more lines of code across thousands of packages that want to use the standard reverse functionality.
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"})
}
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.
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}
}
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
}
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.
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 :(.