Konubinix' opinionated web of thoughts

Go Embedding (Like Subclassing, Inheritance, but Not Totally)

Fleeting

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

https://go.dev/doc/effective_go#embedding

// 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.

https://go.dev/doc/effective_go#embedding

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 }

https://go.dev/doc/effective_go#embedding

embedded elements are pointers to structs and of course must be initialized to point to valid structs before they can be used

https://go.dev/doc/effective_go#embedding

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.

https://go.dev/doc/effective_go#embedding

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.

https://go.dev/doc/effective_go#embedding

when the Read method of a bufio.ReadWriter is invoked, […]; the receiver is the reader field of the ReadWriter, not the ReadWriter itself

https://go.dev/doc/effective_go#embedding

Embedding can also be a simple convenience. This example shows an embedded field alongside a regular, named field.

type Job struct { Command string *log.Logger } The Job type now has the Print, Printf, Println and other methods of *log.Logger. We could have given the Logger a field name, of course, but it’s not necessary to do so. And now, once initialized, we can log to the Job:

job.Println(“starting now…”)

https://go.dev/doc/effective_go#embedding

Logger is a regular field of the Job struct, so we can initialize it in the usual way inside the constructor for Job, like this,

func NewJob(command string, logger *log.Logger) *Job { return &Job{command, logger} } or with a composite literal,

job := &Job{command, log.New(os.Stderr, “Job: “, log.Ldate)}

https://go.dev/doc/effective_go#embedding

If we need to refer to an embedded field directly, the type name of the field, ignoring the package qualifier, serves as a field name

https://go.dev/doc/effective_go#embedding

Embedding types introduces the problem of name conflicts but the rules to resolve them are simple

https://go.dev/doc/effective_go#embedding

First, a field or method X hides any other item X in a more deeply nested part of the type. If log.Logger contained a field or method called Command, the Command field of Job would dominate it

https://go.dev/doc/effective_go#embedding

Notes linking here