Embedding Types Within A Struct
A field declared with a type but no explicit field name is called an embedded field
.
// Golang program to illustrate the
// concept of inheritance
package main
import (
"fmt"
)
type Base struct {
b int
}
func (base *Base) Describe() string {
return fmt.Sprintf("base %d belongs to us", base.b)
}
type Container struct { // Container is the embedding struct
Base // Base is the embedded field
c string
}
func main() {
co := Container{}
co.b = 1
co.c = "string"
// Read the fields within embedded fields
fmt.Printf("co.b: %v - %v\n", co.b, &co.b)
fmt.Printf("co.Base.b: %v - %v \n", co.Base.b, &co.Base.b)
// Call the methods of embedded fields
fmt.Println(co.Describe())
fmt.Println(co.Base.Describe())
}
Output:
// Read the fields within embedded fields
co.b: 1 - 0xc00000c060
co.Base.b: 1 - 0xc00000c060
// Call the methods of embedded fields
base 1 belongs to us
base 1 belongs to us
Shadowing of embedded fields
What happens if the embedding struct has a field x
and embeds a struct which also has a field x
? In this case, when accessing x
through the embedding struct, we get the embedding struct’s field; the embedded struct’s x
is shadowed.
Here’s an example demonstrating this:
// Golang program to illustrate the
// concept of inheritance
package main
import (
"fmt"
)
type Base struct {
b int
tag string
}
func (base *Base) DescribeTag() string {
return fmt.Sprintf("Base tag is %s", base.tag)
}
type Container struct { // Container is the embedding struct
Base // Base is the embedded field
c string
tag string
}
func (co *Container) DescribeTag() string {
return fmt.Sprintf("Container tag is %s", co.tag)
}
func main() {
b := Base{b: 10, tag: "b's tag"}
co := Container{Base: b, c: "foo", tag: "co's tag"}
// Read the fields within embedded fields
fmt.Printf("co.tag: %v - %v\n", co.tag, &co.tag)
fmt.Printf("co.Base.tag: %v - %v \n", co.Base.tag, &co.Base.tag)
fmt.Println(b.DescribeTag())
fmt.Println(co.DescribeTag())
}
Output:
co.tag: co's tag - 0xc000122068
co.Base.tag: b's tag - 0xc000122048
Base tag is b's tag
Container tag is co's tag
Note that when accessing co.tag
, we get the tag
field of Container
, not the one coming in through the shadowing of Base
. We could access the other one explicitly, though, with co.Base.tag
.
Embedding Types Within An Interface
Go does not provide the typical, type-driven notion of subclassing, but it does have the ability to “borrow” pieces of an implementation by embedding types within a struct or interface.
Interface embedding is very simple. We’ve mentioned the io.Reader
and io.Writer
interfaces before; here are their definitions.
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
The io
package also exports several other interfaces that specify objects that can implement several such methods. For instance, there is io.ReadWriter
, an interface containing both Read
and Write
. We could specify io.ReadWriter
by listing the two methods explicitly, but it’s easier and more evocative to embed the two interfaces to form the new one, like this:
// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
Reader
Writer
}
This says just what it looks like: A ReadWriter
can do what a Reader
does and what a Writer
does; it is a union of the embedded interfaces. Only interfaces can be embedded within interfaces.
This says just what it looks like: A ReadWriter
can do what a Reader
does and what a Writer
does; it is a union of the embedded interfaces. Only interfaces can be embedded within interfaces.
The same basic idea applies to structs, but with more far-reaching implications. The bufio
package has two struct types, bufio.Reader
and bufio.Writer
, each of which of course implements the analogous interfaces from package io
. And bufio
also implements a buffered reader/writer, which it does by combining a reader and a writer into one struct using embedding: it lists the types within the struct but does not give them field names.
// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
The embedded elements are pointers to structs and of course must be initialized to point to valid structs before they can be used. The ReadWriter
struct could be written as
type ReadWriter struct {
reader *Reader
writer *Writer
}
but then to promote the methods of the fields and to satisfy the io
interfaces, we would also need to provide forwarding methods, like this:
func (rw *ReadWriter) Read(p []byte) (n int, err error) {
return rw.reader.Read(p)
}
By embedding the structs directly, we avoid this bookkeeping. The methods of embedded types come along for free, which means that bufio.ReadWriter
not only has the methods of bufio.Reader
and bufio.Writer
, it also satisfies all three interfaces: io.Reader
, io.Writer
, and io.ReadWriter
.
There’s an important way in which embedding differs from subclassing. When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one. In our example, when the Read
method of a bufio.ReadWriter
is invoked, it has exactly the same effect as the forwarding method written out above; the receiver is the reader
field of the ReadWriter
, not the ReadWriter
itself.