简体   繁体   中英

What does it mean to specify just a struct type as a struct member in Go?

I know I can write code like this, but I don't know how it works:

type MyTransport struct {
  http.Transport
}

func (myT *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
  return myT.Transport.RoundTrip(r)
}

http.Transport is just a struct, right? It has no name. So how does myT.Transport work? Why do I not have to give the transport a name in MyTransport , such as declaring it like ht http.Transport ?

That's an embedded struct, from http://golang.org/doc/effective_go.html#embedding :

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.

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

TL;DR:

That's go's way of "oop" inheritance, more or less:

type MyTransport struct {
    http.Transport
}

//without this function, calling myT.RoundTrip would actually call myT.Transport.RoundTrip
func (myT *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
    return myT.Transport.RoundTrip(r)
}

This is known as embedding. If you haven't read Effective Go, you should. http://golang.org/doc/effective_go.html#embedding

In a nutshell, the methods of the embedded http.Transport are accessible through MyTransport struct.

That property of the language is called Embedding: http://golang.org/doc/effective_go.html#embedding

If the document is overhelming the relavant parts to understand why you write

type MyTransport struct {
  http.Transport
}

instead of

type MyTransport struct {
  transport http.Transport
}

is this :

// 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)
}

Think it as "copying all the struct definition and their methods to this structure"

Why do I not have to give the transport a name in MyTransport

You have created an anonymous field and as such the unqualified type name acts as the field name

See the spec here for more details:

A field declared with a type but no explicit field name is an anonymous field, also called an embedded field or an embedding of the type in the struct. An embedded type must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM