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

huangapple go评论87阅读模式
英文:

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

问题

我知道我可以像这样编写代码,但我不知道它是如何工作的:

type MyTransport struct {
  http.Transport
}

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

http.Transport只是一个结构体,对吗?它没有名称。那么myT.Transport是如何工作的?为什么在MyTransport中不需要给传输器命名,比如像ht http.Transport这样声明它?

英文:

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?

答案1

得分: 4

这是一个嵌入式结构体,来源于http://golang.org/doc/effective_go.html#embedding:

通过直接嵌入结构体,我们避免了这种繁琐的工作。嵌入类型的方法会自动继承,这意味着bufio.ReadWriter不仅拥有bufio.Reader和bufio.Writer的方法,还满足io.Reader、io.Writer和io.ReadWriter这三个接口。

嵌入与子类化有一个重要的区别。当我们嵌入一个类型时,该类型的方法成为外部类型的方法,但当调用这些方法时,方法的接收者是内部类型,而不是外部类型。在我们的例子中,当调用bufio.ReadWriter的Read方法时,它与上面写出的转发方法完全相同;接收者是ReadWriter的reader字段,而不是ReadWriter本身。

嵌入还可以简化代码。下面的示例展示了一个嵌入字段和一个常规命名字段。

简而言之,这是Go语言中实现"oop"继承的方式:

type MyTransport struct {
    http.Transport
}

// 如果没有这个函数,调用myT.RoundTrip实际上会调用myT.Transport.RoundTrip
func (myT *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
    return myT.Transport.RoundTrip(r)
}
英文:

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

答案2

得分: 2

这被称为嵌入。如果你还没有阅读过《Effective Go》,你应该去看看。http://golang.org/doc/effective_go.html#embedding

简而言之,嵌入的http.Transport的方法可以通过MyTransport结构体进行访问。

英文:

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.

答案3

得分: 2

该语言的这个属性被称为嵌入:http://golang.org/doc/effective_go.html#embedding

如果文档过于庞大,以至于难以理解为什么要写成这样:

type MyTransport struct {
  http.Transport
}

而不是

type MyTransport struct {
  transport http.Transport
}

原因是:

// ReadWriter 存储指向 Reader 和 Writer 的指针。
// 它实现了 io.ReadWriter 接口。
type ReadWriter struct {
    *Reader  // *bufio.Reader
    *Writer  // *bufio.Writer
}

嵌入的元素是指向结构体的指针,当然在使用之前必须初始化为指向有效结构体的指针。ReadWriter 结构体可以这样写:

type ReadWriter struct {
    reader *Reader
    writer *Writer
}

但是为了提升字段的方法并满足 io 接口的要求,我们还需要提供转发方法,像这样:

func (rw *ReadWriter) Read(p []byte) (n int, err error) {
    return rw.reader.Read(p)
}

可以将其视为“将所有结构体定义及其方法复制到此结构体中”。

英文:

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"

答案4

得分: 0

MyTransport中,你不需要给传输器(transport)命名的原因是你创建了一个匿名字段(anonymous field),因此未限定的类型名称充当字段名称。

更多详细信息,请参阅这里的规范

声明了类型但没有显式字段名称的字段是匿名字段,也称为嵌入字段或将类型嵌入结构体中。嵌入类型必须指定为类型名称T或非接口类型名称*T的指针,并且T本身不能是指针类型。未限定的类型名称充当字段名称。

英文:

>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

huangapple
  • 本文由 发表于 2014年6月7日 05:22:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/24090820.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定