The Go language has no classes, so there is no inheritance, so you can only combine various functions by embedding features (for convenience, we will call them embedding).

The simplest is the interface embedding interface, for example, we have defined Reader and Writer interfaces.

1
2
3
4
5
6
7
type Reader interface {
    Read(p []byte) (n int, err error)
}

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

We can then combine them to define a new ReadWriter interface

1
2
3
4
type ReadWriter interface {
    Reader // 嵌入 Reader
    Writer // 嵌入 Writer
}

This is equivalent to the following, but it avoids repeating the definition (Don’t repeat yourself).

1
2
3
4
type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

The Go language also supports embedding interfaces in structs, but this requires a bit of understanding.

If we define a struct called ReadWriter, we want it to implement the Reader and Writer interfaces, and we already have instances of the corresponding reader and writer. Without the embedding feature, we might have to look like this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type ReadWriter struct {
    reader *Reader
    writer *Writer
}
func (rw *ReadWriter) Read(p []byte) (n int, err error) {
    return rw.reader.Read(p)
}
func (rw *ReadWriter) Write(p []byte) (n int, err error) {
    return rw.writer.Write(p)
}

We define Read and Write methods for ReadWriter, but then we don’t do anything specific, we just delegate the functionality to reader and writer objects. Obviously, there is no point in writing this code over and over again. We can use the embedding of the go language to eliminate this repetition, so we have this code

1
2
3
4
type ReadWriter struct {
    *Reader
    *Writer
}

Do you see the difference? Yes, the field names reader and writer are removed from the front. At this point the compiler knows that you want to embed the Reader and Writer pointer into the ReadWriter structure. If you now have a rw *ReaderWriter pointer, you can call the rw.Read() and rw.Write() methods directly, as if ReadWriter had defined them itself.

But how do you initialize a ReadWriter structure without a field name? This is where it’s easy for beginners to get confused. For embedding, the go language convention is that the field name is equivalent to the type name (minus the package name) and does not need to be specified on display.

That is, ReadWrite will have a property called Reader and a property called Writer, by which we can initialize ReadeWriter, for example

1
rw := &ReadWriter{Reader: r, Writer: w}

We have two ways to call the embed method

1
2
rw.Reade(p)
rw.Reader.Read(p)

The first can be called directly via rw, and the second can be called via the agreed-upon rw.Reader. Generally speaking, the first call is the one you want to use for embedding.

However, what if the Reader interface has a method that is also a Reader? Then a naming conflict arises

1
2
rw.Reader() // 调用 rw.Reader 的 Reader 方法
rw.Reader   // 读取 rw.Reader 实例

To eliminate this conflict, the go language specifies that the fields and methods of the outer struct take precedence, as it is written

First, a field or method X hides any other item X in a more deeply nested part of the type.

That is, if the Reader interface has a method that is also Reader, then rw.Reader refers to the Reader instance, and you cannot call the corresponding Reader method with `rw.

In fact, there is another conflict: ReadWriter embeds Reader and then owns a Reader field. But what is the problem if ReadWriter also defines a field named Reader?

Second, if the same name appears at the same nesting level, it is usually an error; However, if the duplicate name is never mentioned in the program outside the type definition, it is OK.

As long as you don’t reference them, you won’t report an error. Still, I would advise against showing off your skills and writing such incomprehensible code.

Also, the definitive official description of embedding features is here, and there is a Type embedding in Go is also very good.