 We've mentioned before that certain operations trigger runtime errors, what Go calls panics. For example, if we try and access an index which is out of bounds of an array, like we do here where we have this array x, which is an array of three nths, and we try and access index six, well that's outside the bounds of the array. And so we get a runtime error, which Go calls a panic. And here we have this variable of type animal where animal is some interface, and we try and call its method eat. But the variable a here still has the default interface value nil. You can't call methods on nil, so we get another runtime error. As we'll discuss in detail shortly, when a panic occurs, execution backs out of the call chain. So you're in function main, which called foo, which called bar, and a panic occurs in bar. And so execution backs out of bar, then backs out of foo, and then eventually we'll back out of main itself, the original call of the program. And once we back out of main, that's when the program terminates. It is possible though, to queue up functions to be called as execution backs out of the call chain. And so as we go back at the call chain from from bar to foo to main, in each function there might be so called deferred function calls, which will then execute. And in these deferred function calls, if you call the special built-in function recover, then that stops propagation of the panic, and execution resumes in the function which called that function. So say in foo, if there's a deferred function call in which the recover built-in function is invoked, then execution will resume back in main, where main called foo immediately after the call to foo. I'm sure that was hard to follow, we'll show examples. But first, it's important to understand the distinction between these panics, between runtime errors, and what you otherwise might call errors, specifically user input errors and IO errors, input output errors. A runtime error reflects a bug in our code. It's something that happened that should have never happened, but we had a mistake in our code, and so the panic got triggered. The appropriate thing to do is to write our code to make sure that these panics never trigger. You should say never have an index operation on an array where the index is out of bounds. We can always arrange on our code to make sure that never happens. Sometimes that might mean that when we compute some index number, we have to check if it's within the bounds of the array before we do the index operation, something like that, but it's something we can always avoid. In contrast, user input errors, well, users are responsible for that. The user provides you a file or enters data that's bad. That's something you can't control. It's outside of the program. Likewise, IO errors, where we try and read a file or make a network connection, something's wrong with a file, something's wrong with a network. We can't stop that from happening. So these things aren't our fault, but our code needs to cope. It needs to account for the possibility of these things and act accordingly. Sometimes that might mean like just telling the user, hey, your input was bad, try again. All sorts of wonky things happen on the network and you can often succeed by just trying again and it'll work maybe the next time. Things like that, though, there are cases where for reasons outside the control of our program, some kind of input, output error, like we can't read a file that we really need to. And so our program just has to abort. It has to give up. But it is still our responsibility to first detect these errors. The no panic is automatically triggered and automatically backs out of execution for these kinds of errors. You have to actually check for them. And that's what we do with the error type and go, which we always get back from some function we called check if error is nil. And if it's not nil, then some error occurred and we need to account for that. So runtime errors, panics, those are bugs in your code. You need to fix them, other kinds of errors. Those are things outside the control of your program. It's your responsibility to account for their eventuality, but we can't stop them from happening. Strangely, then it is sometimes appropriate to actually deliberately trigger a panic. And the example here is that we have this factorial function where it's expecting input, which has to be a positive integer. There's not a great example because you could always write your factorial function to accept a unsigned in which case it would then be guaranteed at compile time to be a positive integer. But bear with me, there's just an artificial example. Anyway, you have some function like factorial here where we want to constrain the set of possible values that we accept as valid input. And it is the responsibility of the caller to make sure they call this function correctly. So in this example, if someone calls this factorial function with an int value that is less than zero with a negative int value, then that is just a mistake. That is a bad operation on the part of the caller. And so what we might want to do then is detect this case, test if and is less than zero if it's negative, and call the built-in function panic. And to panic, you can just specify any value. It'll accept an empty interface value. And so most commonly just pass in a string describing what the error was. But it could be any kind of value here. And so if in our code anywhere we incorrectly call factorial, well, then at least the problem doesn't hide. It doesn't mean our program will terminate, assuming we don't recover from this panic. It will terminate our program and we'll see on console saying, hey, there was a panic with this value, which is a string reading cannot compute factorial negative number. You might think it's bad to deliberately trigger a program failure. But in any scenario where we can detect that there is a mistake in code, generally then we want to abort the code. Because if we just allow the code to continue on having known it made a mistake, well, you know, once one thing is wrong, that can have all sorts of far-reaching consequences, which could be very bad. So it's generally better just bail out. And also we want to hear about these problems, right? If we can detect that there's a mistake in the code, a bug, we want to hear about it and we want to know where the problem occurred so we can go fix it. And when the panic does occur and it aborts our program, we get a stack trace, a listing of where execution was in the functions. So if a panic occurs in some call to bar and bar was called by foo and foo was called by main, we see that in the stack trace. And in fact, you have the line number of exactly where the call was. So with the stack trace, you can pinpoint exactly where the panic occurred. So I mentioned that we can defer function calls and the way we do so is with the defer statement, which unsurprisingly begins with the reserved word defer and then you have the call in the statement. And it could be a function call, it could be a method call. In these cases, it's all function calls. And so here the statement is deferring a call to cat. Cat is not immediately called right now. It'll be called later. This is just deferring the call. And here we're creating variable i with the value 10. And when we defer a call to dog with the value of i, then this is a call where the value of i will be 10. Then we're changing the value of i to 6. And if we defer a call to dog again with the value of i, well now i has the value 6. And so this call 6 will be passed to dog rather than 10. So be clear. The call itself is not executed immediately into the defer statement, but all the arguments get evaluated. So like you could have some function here, foo, that returns the argument for dog. That is immediately called. The call to foo here is going to be called. It's only dog, the outer call. That's the thing that gets deferred until later. All the arguments, though, get evaluated. Anyway, so in this arrangement, we've deferred cat, then dog, then dog. When execution leaves the call in which we deferred them, in this case the call to main, when execution leaves main, by any means, whether it returns through a return statement or a panic occurs and it propagates out of the function, by whatever means execution leaves this call to main, the deferred functions will execute in reverse order. So the last one deferred is the first one executed. So first it's going to be called a dog where the argument was six, and then it's going to be this deferred call where a dog was 10, and then the call to cat. And be clear that these defer statements are actually executed actions that actually queue up the function call. And if they're skipped over for whatever reason, like there's a branch where the defer only happens conditionally, well then that call does not get queued up. Like imagine here, there's some panic or return that occurs before we get to the last defer. Well in that case, when execution leaves the call here, only this first call to dog has been queued up and then the call to cat. So it would execute dog with the argument 10 and cat with no arcs, but not do this one because we never got to deferring this before we left the function. Also we mentioned that when a panic is triggered, execution backs out of the call chain. So if we're in some call bar called by foo called by main, if a panic occurs in bar, the deferred calls in bar will execute and then the deferred calls in foo will execute and then the deferred calls in main will execute. It'll go in reverse order of the call chain. So here for example, in main we're calling foo which defers this call to cat and foo calls bar. So we come up here in this deferred dog and then in bar a panic is triggered. Here we're doing so deliberately just for demonstration purposes. Obviously not a realistic thing to do, but we can do it. And so execution will leave this call to bar and all of this deferred calls will execute in this case the call to dog. So we're gonna see bark and then execution will back out of the call to foo. And so it's deferred calls will execute in this case just this call to cat. So we'll see meow and then execution backs out of main and this line here never executes. Be clear, when a panic triggers normal execution stops until we recover in this case we never recovered and so the only code executed once the panic happens is all the deferred calls and nothing else will execute. So here's an example of recovering from a panic. We have this function main, we call foo and it's return value stored in Z and we then print out this statement with the value of Z. But what's gonna happen in the call to foo is we're gonna trigger a panic and in foo here, let's see, we are deferring a call to cat, deferring a call to dog, then we trigger a panic and because the panic is triggered here unconditionally it's always gonna be triggered. Again, not a realistic thing to do but it is legal code. And so execution of foo is always gonna stop here with a panic. This stuff will never execute and the deferred calls get executed in the reverse order of how they got deferred. So first they call to dog, we'll print out bark and then the call to cat will print out meow but then it also calls the built-in function recover. There's another built-in function just like panic. And if we call recover inside a deferred call then that stops the panic and the question is, well, where does execution resume? Well, we recovered from the panic in a call deferred inside foo. So we go back to where foo was called and that's where execution resumes. It resumes as if foo has just returned. The question though is, well, what is foo returning? Normally foo is supposed to return an int value five but because we recovered it's never gonna reach this return statement. So instead the call returns the default value, the zero value fourth return type which in this case is just the value zero. So the call to foo here returns the value zero. Execution picks up from there. The value zero is assigned to Z. We then print after with the value zero. So be clear. You have to use recover in a deferred call and when we recover in a deferred call execution resumes with the call which deferred that call returning with default zero values. Also understand that when recover is called that doesn't immediately end the deferred call which is executing. So in cat when we call recover that call finishes out and recover actually returns a value. It'll return the value which we passed to panic. So in this case we passed the string abort to panic and so that's what recover is going to return here and that's what's gonna be assigned to V. Understand though that to the compiler panic accepts an empty interface value and recover returns an empty interface value. So that's what the type of V is going to be here. It's an empty interface type. Handily though the print line function does some type discovery at runtime and it inspects what the thing is to figure out oh it's a string so it knows how to print a value of type empty interface. So it still knows how to print out the string even though it's an empty interface value being passed in here. Now when using recover there's a strange thing you have to keep in mind and that is recover will actually do nothing. It will not recover from the panic if it is invoked in a function that isn't a deferred call triggered by a panic. So here in a call domain we're calling foo foo refers bar and then we trigger a panic and so okay in bar what are we doing but we're calling this act function and act is calling recover here. But the call to act here was not deferred and triggered by a panic it's called by bar. The call to bar though was a deferred call triggered by the panic and so the call to recover here in bar that does work that does stop the panic. So this recover up here does nothing returns nil. This recover stops the panic actually returns the value that was passed to the panic function and so it returns the string abort. Honestly I'm not super clear on what the point of this rule is I don't think it's stemming from some technical limitation. I think it's a rule to encourage you to use recover as only certain ways to not use them in unexpected places. So in fact the way we use recover when we do use it is almost entirely in this style. Here we're deferring an anonymous function which we're then immediately calling that's what the syntax is. We're defining a function that's what this expression returns is this new function that calls recover and then we put parentheses after to immediately call it because remember defer is expecting a function call not a function definition. So that's why we want the parentheses here. One more small thing we can do and go is give our function so-called return parameters. Here for example this function foo is a function which has a parameter a of type int and then it returns an int but we've put the return type in parens and made it a return parameter called x. x here is really just an int variable called x and its initial value is just the default zero value which for an int would be the value zero but we can then otherwise use it like any other variable. Here we're assigning the result of five plus eight to x. What's special about a return parameter is that well normal in this function foo returning an int, a return statement would have to specify an int value as we do here. This expression here has to be an int value but if your function has return parameters then you can have a return statement with no values and it defaults to returning the return parameters. So in this case we just have the one return parameter so this return with no value is implicitly returning x. In this very small artificial example there's no real clear benefit to the return parameter. It's just a stylistic thing of how we chose to create this variable and it gives us the very slight convenience of only having to write return here instead of write x. Here's a very similar example where the function bar takes an int and returns to int and the first return value is a parameter called x and the second return value is a parameter called y. And again we can assign to them and use them like any other variable. Their initial value is zero but here we're assigning to x the value of five plus a. Here we're assigning to y the value of five times a. And when we return from this function we can return explicit values as we do here. We're specifying an int value 10 and then another int value in this case the value of y. It's just a coincidence that y is in the position where the return parameter is. It can be anything just any int expression when x is also acceptable there. Just any int value. But if we write return with no values then implicitly it's returning x and y, the two parameters. So again in this example having the return parameters is just a very, very slight convenience. It's just a stylistic thing. There is one case though where return parameters can do something very useful for us and that is they will effectively allow us to control what gets returned from a function where a panic was triggered in the function but then got recovered in the function. Assume that we do some stuff down here and some panic occurs but then this deferred function recovers. Well then normally execution would resume by returning the default int value zero from this call to foo and that's where execution would pick up. But say we wanted and even in the event of a panic we want to control what foo returns. Here we've arranged that. We've given the function foo a return parameter called x and then in our deferred function where we recover. If recover returns the value nil that's what this is testing for. Well then either there was no panic or a panic was triggered by calling the built-in panic function with the argument nil passed in which would be a quite strange and rare thing to do so probably not something we'd expect and we can kind of discount that possibility. So we can reasonably safely assume that if recover returns nil then there was a panic and recover has just stopped the panic and as we discussed earlier an anonymous function has access to the variables of the enclosing scope. So in this case the anonymous function has access to the parameter x of foo. It is just a local variable of foo like any other local variable and so we can assign to it here inside the deferred function. And so having now set the return parameter to a value when the recovered call to foo returns it's gonna return five not the default value zero. So that is the one truly useful thing that return parameters can do for us. Otherwise using return parameters is really just a stylistic thing.