Welcome back.
In this lecture you're going to learn what happens when you pass an ordinary value like an integer, a slice
or a map to a function.
First of all let's take a look at the design diagram again.
You have finished half of the job. As you can see updating the results is a unique responsibility.
It's a job on its own.
So in this lecture you're going to move the update logic to a new function.
OK let's take a look at the main function. Here is the update logic.
It saves the unique domains to a slice and it increases the visits per domain.
And it also keeps track of the total visits.
So it updates the parser in the loop.
So right above the update logic,
I'm going to call a function instead.
Now I'm going to send the parser and the parsed result to the function. You'll see in a second,
why the function needs them.
OK, I'm going to cut the updating logic from here.
Inside the parser, I'm going to declare the update function and I'm going to paste the code here.
You know that the function needs to take parser and parsed values.
So I'm going to declare their types as input parameters like so. As you can see, the update function gets
a parser value and it uses the parsing result to update the parser.
Oops! It doesn't work.
Let's find out the problem.
Remember when a struct value is assigned or passed to a function, it gets copied. Here,
the parser value is a copy.
It is not the original parser value of the main function. To understand the problem better,
let's dump the parser value right before calling the update function like so.
I'm also going to dump it after the function call. So you can see what changes in the original parser
value. And inside the update function,
I'm going to put another one as the last statement, so you can see what changes in the local parser value.
Let's open the log.txt file.
And leave only a single line here. So I can easily debug it. The parser value is zero in
the main function. Next,
it sends the value to the update function.
So the update function updates its local copy.
Lastly, the update function returns. For the main function, the domains and total fields don't change. Only
the sum field changes.
Let's investigate this problem further.
Here is the parser value of the main function.
As you can see, it starts with zero fields.
Here is the update function.
It accepts the parser and result values.
The main function sends its own parser value to the update function.
So the update function receives the parser value.
However this value is a copy.
So it's not the original parser value.
Remember every value in Go gets copied.
Go is a 100 percent pass by value language.
That's why the update function receives a copy. The update function changes the copy using the result
like so. It adds the result to the sum field. It adds a domain name to domains fields, also updates the
total field.
So when the update function is about to return, this is what the parser value looks like. But the main
function never sees the changes because the update function only changes its local parser copy. For
the main function, only the sum field changes.
But why?
Let's talk about how this works behind the scenes. By the way, to better understand what I'm going to
explain,
you should have watched the slices and map internals lectures.
Remember, there are two parser struct values.
One is in the main function and the other one is in the update function.
Let's take a look at the main function's parser value first.
Here is the sums map field.
Remember a map
value is a pointer. So it stores a memory location.
Here it points to the memory address 40.
Let's see what it points to.
As you can see, it points to a map header. A map header does some magic and lets us work with key value
pairs like so.
So under the hood, a map value is just a proxy.
And it points to the memory location of a real map.
Now let's take a look at the update function's parser value. As you can see, it points to the same memory
location.
It's because it's been copied from the main function's map value.
So, both of the map values point to the same memory location.
So in summary, when you copy a map value, it will continue to point to the same memory location.
There are two different map values but the main and the update functions behind the scenes point to the same
map header.
That's why the main function can see the updated map.
However, the main function cannot see the changes to the other fields (of the parser struct value).
Let's see why.
Let's first take a look at the original parser value.
This is the domains field. As you can see, it doesn't point to a backing array because it's a nil slice.
So the length and capacity fields are zero.
Now let's take a look at the update function's parser value. As you can see,
This time, the domain field points to a memory location. The value of length and capacity fields is
1. It's because there is a single element in the backing array.
But why doesn't the main function see the new element?
It's because the update function can only change its local slice header. However, it never returns that slice
header to the main function.
That is why the main function cannot see the new element. All it sees is a struct header with zero fields.
So in summary, when you change a slice value in a function,
you need to return it.
So the calling function can see the changes to the slice header.
Okay let's get back to the code now.
So what is the solution?
One of the solutions is returning the copy from the update function like so.
I also need to declare a result type.
Now let's get back to the main function here.
I'm going to override the original P variable with the returned copy like this.
As you can see now it works. Before the call,
the parser was 0.
Here the update function updates its local copy and it returns the updated copy.
So the main function receives the copy and updates its own copy.
There are three copies of the parser value here.
This is the first copy.
This is the second copy.
This is the last copy.
The last one is also a copy because when you return a value it gets copied too. OK, let me remove the
debugging calls.
Let me also put the records back to the log file.
OK, it works, awesome.
Now you know how to create functions and how to use them to split a program into functions.
You have also learned about the pass by value semantics.
In the next section, you're going to learn how to work with pointers.
See you there.
Bye!