Welcome back!
Lastly, you learned how to use pointers with the log parser program.
ou are going to understand why you might want to use only the pointers for all the functions
that work with a parser value. Before talking about the value versus pointer semantics,
let's refactor the error handling.
Let's take a look at the main function.
Why does it need to check for an error on every iteration.
Doing so is not clear.
Let's do it only once.
So I'm going to remove the error handling from here.
So what is the best way to detect the error
only once. Remember, I'm using pointer mechanics here, so I can save the error in the parser value and I
can check for it only once.
Now I'm going to print the error only once after printing the results like so. As you can see, here,
I'm getting the error from the parser. lerr means
the last error, by the way, you'll see how it works in a minute.
So let's go to the parser declaration, and add an error field there.
Now it cannot detect the errors.
Let's go to the parse function.
First, I'm going to remove the error result value.
Next, I'm going to save the errors to the parser
like so. I also need to declare an err variable here because now I don't declare it in the result
value.
Let me also remove the return from here because the next return already returns.
Now let's get back to the main. The parse function returns a single value.
So I'm going to remove the blank identifier from here.
Cool!
Now it can detect and print the error.
However, it continues parsing and updating the parser.
That is why it prints a negative result here.
So let's prevent it. To do that,
let's go back to the parser.
I'm going to put a guard here, as the first statement like so.
So if there was a previous error the function will simply return.
Now there is a different output but it's still not good.
It's because when an error occurs, I also need to prevent updating the parser.
So let's go to the update function, and put a guard also there. Awesome! When it detects an error
it doesn't process the rest.
Let's go back to the main function.
I'm going to introduce another function for the errors like so. It takes a slice of error values.
Let's move the error handling like so. Instead of checking errors like this, let's do it in a loop.
If there is an error, I'm going to print it like so.
Let me remove the rest of the code as well.
Now I'm going to call it from the main function. First,
I'm going to create an error slice and I'm going to get the error from the scanner.
Lastly, I'm going to get it from the parser. Cool, it works!
I can handle the errors like so because in Go,
errors are values. But I think exposing the error value is not good.
I can introduce a function to do that, so that, I can control what the return. In addition to that, you
should always try to expose the struct fields as little as possible, so that, if you change one of them (the fields)
later on, the other code won't break.
Okay, let's introduce the function in the parser.
I need to get the error from the parser. So, I'm going to declare the parser as an input value. Here,
I don't use a pointer because I don't need to change the parser.
I'm only going to read from it.
I also need to return the error.
So I'm going to declare an error result value.
Lastly, I'm going to return the error from the parser like so. OK. Let's use the function from the main
function. Here,
I'm going to get the error from the error function like so. Excellent!
It works!
OK. Now it's time to talk about whether a function should accept a pointer or a bare value.
Let's take a look at the parser functions.
Let me fold all the functions by selecting "fold all" from here.
OK.
Let's take a look at the functions that use value semantics.
newParser returns a parser value.
err takes a parser value. We use value semantics because these functions do not change the parser values.
Now let's take a look at the functions that use pointer semantics.
Both the parse and update functions take a pointer to the parser.
These ones use the pointer semantics because they change the parser values. For example, the update function
changes
the given parser value like so. As you can see, all of these functions work with the same type: parser.
However, they are inconsistent because sometimes they take pointers sometimes they take values.
When you're building a program.
Your first goal should be being consistent.
It is better to use the same semantics. Since the parse and update functions change the password values,
let's change all of these functions to work with pointers instead.
First, I'm going to return a pointer from the newParse function like so. Next,
let's change err function to take a pointer instead. By the way,
When you return a pointer from a function, as in here,
the runtime will keep the pointed value alive until the pointer variable goes out of scope.
Let's go to the main function.
Now the P is a parser pointer.
So let's send p values directly.
Let's also accept a pointer in the summarize function.
It works! Good.
I think the program is consistent now.
Notice that, I only use pointer semantics for the parser type but I use value semantics for the result
type.
It's because, throughout the program I only use the result type as a (bare) value. So being consistent is the
key.
While refactoring the code, let's go to the parser and change some names. You know we Gophers don't like
long names in shorter scopes.
For example, the parse function's result value is already clear: It returns a parsing result.
Now, I'm going to rename it like so. Let's also change the update function's result value in the same way.
Next, I'm going to remove these declarations.
Instead, I'm going to get them from the parsing result directly.
First, I'm going to get the domain name from the parsing result like so.
OK
Now I'm going to get the number of visits from the parsing result as well.
Now let's go to the summarize function. Here,
I'm going to directly get the visits from the map
like so.
Good.
It still works. Congrats!
Now you know how to work with pointers and memory addresses.
You've
also learned when to use pointers,
and why do you need to use them. See you in the next section!