Why set a variable by reference (&) over a simple assignment

Hello all Gophers,

Did you know why we have to set a variable by reference (&) over a simple assignment ? I see Go Doc on HTTP (https://golang.org/pkg/net/http/) and don’t see exactly why the author had chosen this form :

client := &http.Client{
    CheckRedirect: redirectPolicyFunc,
}

over this :

client := http.Client{
    CheckRedirect: redirectPolicyFunc,
}

I’m aware on what is a Pointer, what is a Reference… But it’s not clear to me why it should be interesting to declare a new variable by reference. I have habit to add “&” when used on a function waiting for a Pointer. But in the example, Client is not used with function call… So, I don’t see why the author added “&” in assignment.

Complete code from Go Doc here :

client := &http.Client{
	CheckRedirect: redirectPolicyFunc,
}

resp, err := client.Get("http://example.com")
// ...

req, err := http.NewRequest("GET", "http://example.com", nil)
// ...
req.Header.Add("If-None-Match", `W/"wyzzy"`)
resp, err := client.Do(req)
// ...

Many thanks for your replies

Hey @ludovicdeluna,

I’m no expert, but my best guess is that http.Client is passed around as a reference due to the fact that it carries along information such as cookies which to pass around without a reference would be expensive instead of just passing around a pointer to everything.

1 Like

Since all of http.Client’s methods are declared on *http.Client it’s one less level of indirection you would have to reason about. You could declare it without the & though and it would work for the same reason but with the cognitive overhead every time you needed to pass it around (since passing it around would make a copy).

2 Likes

I feel like it is incorrect to refer to Go having references via the & character. Go doesn’t have references in the C++ sense. Only pointers. You can take the address of another value. Or you can copy that value to a new variable.

The closest I have heard is when the terminology “reference types” is used in Go, to describe maps and slices. But these are really just implying that you are copying around pointers and not values.

I hope I said this correctly.

1 Like

Sorry … You did right @Justin_Israel ; In Go, there are only Pointers. I found this post about Pointers… And references : http://spf13.com/post/go-pointers-vs-references/ ; I’m so used with “&” in other languages (as the use of keyword “new”, really strange when you come from Ruby and alike). But yes, no references in Go. Only pointers with high flexibility.

1 Like

Thanks @freeformz and @radovskyb : It’s seems to be a useful optimization for memory usage. The use of “&” on assignment make the code more readable in this case. Many thanks.

1 Like

Don’t take it as a blanket “optimization”. Profile your code with benchmarks (built into testing). The Go vm is very good at copying data around. When you take a pointer to something, that something is likely going to end up on the heap. Once that’s the case it needs to be considered for GC. I find that most time spent optimizing Go code is spent reducing allocations. Also a pointer is an additional allocation (one for the pointer, one for the struct it’s pointing to). And pointers ARE copied when passed to a function (everything in go is passed by copy, even pointers). FWIW: I’m not a big fan of the “Pointer ALL the things” that seems to be happening a lot.

1 Like

Don’t worry about it in this case. Usually the only times it matters is when you need to pass something into a function that takes a pointer or when you want that object to implement an interface, but the methods on the object which implement the interface take a pointer receiver. In either case, you’ll get a compiler error, so you won’t have to worry about making a mistake.

1 Like

Thanks for you reply. Yes, I see lot of code on Github that use more and more myVar := &T{…} or like. So I ask me if this was a good things for optimization. I’m not aware on the Heap allocation when using pointer, but it’s interesting to mention it. Thanks.

Thanks @Craig_Weber. I faced a similar problem with one of my code. I’ve been disappointed by the fact that to remember witch method need pointer and witch method need a copy of the object (without goes to it’s signature). While designing a lib, it’s not so easy to make good choices. I finally choose to use pointers everywhere to be more “easy to use”. This is what @freeformz said about the use of pointers everywhere, perhaps a bad practice… But I can’t use “!” in the function name like a do with Ruby to say “Be aware, this method will change your object instead return a copy”.

I don’t know how much of a difference it makes, but I prefer to assign variables to values (sometimes this isn’t possible if you’re assigning a variable to the result of a function that returns a pointer–e.g.,: s := bufio.NewScanner(r)) and pass by copy whenever possible. I only pass by pointer if the method will mutate the original value. This has worked very well for me.

1 Like

So if I did it by pointer (&), then creating another client later in the code to make another http request, wouldn’t that client then also already be using the same settings ? In this case -

CheckRedirect: redirectPolicyFunc,

Example:

client := &http.Client{
	CheckRedirect: redirectPolicyFunc,
}

resp, err := client.Get("http://example.com")
// ...

req, err := http.NewRequest("GET", "http://example.com", nil)
// ...
req.Header.Add("If-None-Match", `W/"wyzzy"`)
resp, err := client.Do(req)
// ...

resp2, _ := http.Get("http://url.com")
// ...

So would resp2 be using the “redirectPolicyFunc” set when “client” was setting it ?

Sorry I’m a newbie and I’m not really a programmer (per sé) :slight_smile:

Thanks!
Alex

This line does not use the client declared above, so no.

1 Like

Hmmm… ok but doesn’t http.Get use http.Client further down the line? (Haven’t looked at source for this) And if that is the case doesn’t the &http.Client set the same setting for everything that then uses it? I’d have thought so, since ‘client’ is referencing/ pointing to http.Client and changing it?

Thanks!
Alex

http.Client is a type, so

client := &http.Client{
	CheckRedirect: redirectPolicyFunc,
}

allocates a new variable of that type and a pointer to it. It does not affect any other usage of http.Client. Internally, http.Get uses http.DefaultClient which is another *http.Client.

Thanks for your patience with me :slight_smile: - ok so maybe it makes more sense to me if I compared it to a object/class … am I understanding correctly that doing the above is like instantianting a new object from the “class” http.Client but instead of making a copy of it, it points to the original object with the additional parameters you’ve given it which are only applied to it in the “client” variable ?

Does that sound remotely correct ish?

Thanks again!
Alex

Ok, so for me to make a little more sense of this, I played around a little and made this:
https://play.golang.org/p/IYtwwf124q

What I still don’t get is, why is it used as &http.Client vs http.Client if the result is (seemingly) the same? I guess pointing to the type Client is more memory efficient, but then if I’m just pointing to the client, why/how does it not modify it ? :confused:

Thanks!
Alex

The results are different, especially when passing the variables around. Giving a http.Client to a function will give them a copy of the object, which they can modify without it being visible from the outside. Passing a *http.Client gives them a pointer to the object, so any changes made are made to the original.

Saying foo := &http.Client{} is equivalent to saying foo := new(http.Client) which you may recognize from other languages. Saying foo := http.Client{} has no real equivalence in a language like Java which is always pointer/reference based. But if you did, you can gain a pointer to the object with fooPtr := &foo.

Follow this tour along for a couple of steps and things may clarify: https://tour.golang.org/moretypes/1

ah … and the “original” in this case would be foo of type http.Client with the custom CheckRedirect function.

So when some other function receives foo and changes CheckRedirect to be something else, the foo object will contain that change from that point forward, without having to shadow/overwrite foo with a returned http.Client type.

is that correct-ish :slight_smile: ?

thanks!

There are two foos in my example, so it’s unclear for following discussion. But when you’re saying foo := &http.Client{...} foo is a pointer to a http.Client - you can give that pointer to anyone and it will refer to the same object, so whoever follows the pointer to read or change a value will see the same thing. If saying foo := http.Client{...} foo is the http.Client value, and when giving that to someone you are giving them a copy of the entire http.Client. What they do with it thereafter, or what you do with it, doesn’t matter to the other because you are each dealing with separate copies of the value.