为什么http.Client{}前面有一个&符号?

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

Why is http.Client{} prefixed with &?

问题

我正在学习Go语言,并阅读Go的官方文档关于net/http的部分,我从文档中复制了以下代码进行测试:

package main

import (
	"net/http"
	"fmt"
)

func main() {
	client := &http.Client{}
	resp, _ := client.Get("http://example.com")

	fmt.Println(resp)
}

http.Client是一个结构体,但我不知道为什么前面有一个&指针前缀。我认为创建一个http.Client的引用是不必要的。而且为什么client变量有一个Get方法?我正在阅读net/http的源代码,它定义了下面的Client结构体:

type Client struct {
	Transport RoundTripper
	CheckRedirect func(req *Request, via []*Request) error
	Jar CookieJar
	Timeout time.Duration
}

Client结构体没有定义Get方法,为什么client变量有一个Get方法?

英文:

I am learning Go and I am reading Go's official documentation about net/http, and I write following code from doc for test:

package main

import (
	"net/http"
	"fmt"
)

func main() {
	client := &http.Client{}
	resp, _ := client.Get("http://example.com")

	fmt.Println(resp)
}

http.Client is a struct, but I do not know why there is a & pointer prefixed. I think creating a http.Client reference is not necessary. And why does the client variable have a Get method? I am reading the source code of net/http, it defines the Client struct below:

type Client struct {
	Transport RoundTripper
	CheckRedirect func(req *Request, via []*Request) error
	Jar CookieJar
	Timeout time.Duration
}

The Client struct does not have a Get method defined; why does the client variable have a Get method?

答案1

得分: 20

我会帮你翻译以下内容:

我首先建议你参加Go Tour,以了解该语言及其基本语法。

你引用的类型声明只包含结构体的字段,而不包含其方法。方法在其他地方定义,类似于函数,但添加了一个接收器(receiver),用于指定它们所属的类型。例如,Client.Get() 方法的定义如下:

func (c *Client) Get(url string) (resp *Response, err error) {
    req, err := NewRequest("GET", url, nil)
    if err != nil {
        return nil, err
    }
    return c.Do(req)
}

方法名前面的部分称为接收器,用于指定方法所属的类型(在这个例子中是 *Client)。详细信息请参阅规范:方法声明

& 是一个取地址运算符,它获取操作数的地址。在这种情况下,局部变量 client 的类型将是 *http.Clienthttp.Client{} 是一个复合字面量,它创建了一个 http.Client 结构体类型的值,并且 & 取得了存储此结构体值的匿名变量的地址:

取复合字面量的地址会生成一个指向具有该字面量值的唯一变量的指针。

这样做是为了使 client 变量成为指向 http.Client 值的指针,鼓励共享和重用:

客户端的传输通常具有内部状态(缓存的 TCP 连接),因此应该重用而不是根据需要创建客户端。客户端可以安全地供多个 goroutine 并发使用。

如果 client 是一个指针,你可以自由地将其传递给其他函数,只会复制指针值,而不会复制指向的 http.Client 结构体,因此结构体本身(http.Client 值)将被重用。如果不使用指针,如果将其传递给其他函数,将复制结构体本身而不是重用。

请注意,在这个简单的例子中,实际上并不重要,因为即使 http.Client 的所有方法都是使用指针接收器声明的,你仍然可以在非指针变量上调用指针方法,例如 client.Get() 相当于 (&client).Get()。这在规范:调用中有提到:

如果 x可寻址的,并且 &x 的方法集包含 m,则 x.m()(&x).m() 的简写形式。

因此,即使在这个简单的例子中不需要使用 & 地址运算符,养成使用它的习惯是好的,因为如果示例变得更复杂,或者你编写的代码确实需要这样(例如,你传递创建的客户端),这将很有用。

英文:

I would really take the Go Tour to get a feeling of the language and its basic syntax first.

The type declaration you quoted only contains the fields of the struct, but not its methods. Methods are defined elsewhere, like functions but with a receiver added which designates the type they belong to. For example the definition of Client.Get() method is this:

func (c *Client) Get(url string) (resp *Response, err error) {
  	req, err := NewRequest("GET", url, nil)
  	if err != nil {
  		return nil, err
  	}
  	return c.Do(req)
}

The part before the method name is called the receiver, and that designates the type the method belogns to (*Client in this example). See Spec: Method declarations for more details.

The & is an address operator, it takes the address of its operand. In this case the local variable client will be of type *http.Client. http.Client{} is a composite literal which creates a value of the struct type http.Client, and & takes the address of the anonymous variable where this struct value is stored:

> Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal's value.

It is used so that the client variable will be a pointer to an http.Client value, one that is encouraged to be shared and reused:

> The Client's Transport typically has internal state (cached TCP connections), so Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines.

And if client is a pointer, you are free to pass it around to other functions, only the pointer value will be copied, not the pointed http.Client struct, so the struct itself (the http.Client value) will be reused. Should you not use a pointer, if you would pass it to other functions, the struct itself would be copied and not reused.

Note that in this simple example it doesn't really matter, as even though all methods of http.Client are declared with pointer receiver, you can still call pointer methods on non-pointer variables, as client.Get() would be a shorthand for (&client).Get(). This is mentioned in Spec: Calls:

> If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m().

So even though the & address operator is not needed in this simple example, it's good to keep the habit of using it, should the example grow or should you write code where this does matter (e.g. you pass around the created client).

huangapple
  • 本文由 发表于 2017年8月18日 16:06:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/45751608.html
匿名

发表评论

匿名网友

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

确定