All about the Go errors package

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi I'm Jonathan Hall today I want to talk about the errors package in go so I recently did a video about sort of the history of the errors package and particularly how to wrap errors and go but today I wanted to dive in a little bit more just to talk about what the standard library's error package gives us uh everything not just wrapping errors of course to do this the first place to look is the godoc for the package now there's a lot of information in this documentation it's more sort of a overview of how to use errors and how to check for errors and so on I'll get to some of that but first I want to scroll down here to the index page that shows us the functions in the errors package you can see this is actually a very simple package it only has four functions defined in it there's also the error interface which is not actually defined here because it's actually part of the go spec let's look at that quickly as I've mentioned before the ghost back is actually surprisingly accessible but what we're interested in today is errors and so we can see here there's a pre-declared type called error and it's defined as this interface with a method called error that returns a string so that's really all an error is in go it's any value of a type that satisfies this interface this very very simple interface the fact that it's an interface has some very profound implications that we'll get into a little bit in this video and probably more in a future video but for now just understand that the error type is an interface that it matches this very simple definition and it's built into the language itself it's not defined in the errors package that is it is a fundamental part of the language so with that definition of the error type in mind let's look at the errors package and see what it gives us most likely you are very familiar already with the new function in this package because this is a very common way to create a new error you can just call errors.new with some text and it returns an error that satisfies the interface we just described and when you call that error method on that value you get the string that you pass into the errors.new function I can demonstrate this with some very simple code here I have an incredibly simple program all it does is create an error variable using errors.new and then print out the result of error the error method on that type if we run it we get the expected output so it's a very very simple example using errors.new so why would we ever want anything else well let's let's look at the documentation and see what what it does so the other three functions are all related to error wrapping and unwrapping uh what is that uh you may want to watch my previous video that talks about that to some extent but to get straight to the chase uh error wrapping allows you to take an error add some context to it and return it such that it is possible to get back the original error let me demonstrate what that looks like here I've created a very trivial and ridiculous example but it illustrates the point here my function Foo always returns an error that error says Foo had an error function bar calls Foo if food returns an error which of course it always does in this example then it returns a new error that wraps the existing error and it does that using the fmt.rrf method which I'll talk about before the video is over and then it returns that now if we execute this code we'll see that the output says boo had an error colon Foo had an error why is this special we could we could get the same just by uh wrapping the error.error right we can do that do the same with this if we save this we'll we will indeed see that it has the exact same output so why would we bother with wrapping the error this is where the other functions in the standard libraries error package come in handy so let's look at those let's first look at unwrap because that's sort of the core of the rest of this the description here says that unwrap Returns the result of calling the unwrap method on err if err's type contains an unwrap method returning error otherwise unwrap returns nil okay let's break that apart a little bit this is actually described a little bit more in the top of the document but let's let's scroll up there and have a look here we talk about a sort of implicit interface that can be added to the standard error interface you remember that one error returns string so if an error type also includes a method called unwrap that returns an error then it is said to be a wrapped error and then you can use this unwrap function on it so that's what it's talking about here when it says if errors type contains an unwrap method what it's saying is if this error value that's input into the unwrapped function also has the unwrap method on it then it calls that method that's pretty straightforward I I think now that we've explained it let's look at an example so here I've modified my original main function to try to unwrap the ER value let's execute this uh before I do this what do you think it will do is that what you expected of course the the first print line Returns the original string which is Boo hat and error colon who had an error then I unwrapped the original error and it gets nil notice I still have I'm still using the percent s here and I'm passing in the value error.error let's change this back to what I had in my original example where I use percent W and pass the error value itself now if I save the file and re-execute now you can see that that calling errors.unwrap in my main function here actually unwraps the error so I'll get the original error back the one that Foo returned it says Foo had an error now there are many times when you may want to unwrap an error now an error can be wrapped multiple times and this is where the As and is functions that are part of the standard library's errors package come in handy uh suppose that you have a an error that may be wrapped zero one two three a thousand times and you want to check uh all of the those sort of children errors that's when you'll want to use one of these uh functions so let's have a look at those next is this probably the simplest one to understand um even though it's probably used less often than as but we'll start with that one so is reports whether or not the input error the one you pass to the is function uh matches Target so basically you pass two errors into is and it it's sort of an equality check kind of uh you can think of it as an equality check but there are some special rules to consider uh let's do the equality check first though let's let's demonstrate that and then I'll talk about some of the nuances for my demonstration here I'm changing my Foo function to return io.eof uh that's not a particularly special error it's just nice to have a package variable uh or constant that I can use for this demonstration um because I want to do an equality check so I need to be able to check it against some specific value so that's the only change I've made so far is that food now returns io.eof rather than my custom error let me demonstrate what it's doing so now you can see that I'm returning boo had an error called an eof and I unwrapped that and you get just eof so now we can do our errors dot is check so what I'm doing here is I'm calling errors.is on the original error that I have returned I haven't unwrapped it yet you see so I'm just passing that error which contains the wrapped error inside of it to aerosa is and then I'm also passing io.eof and I want to compare for equality and if they're equal I should output this error message or this message I expect it will work let's let's see indeed it does say yep it's an eof so errors.is has successfully Traverse the tree or or chain of Errors unwrapping is necessary to see if there's an equality match and there is now the extra Nuance comes in the fact that you can actually add a special method to your error type to do that equality check for you and this is useful if for example your error type contains an error code and you want to be able to check for the error code for example if you're interested in doing that it's just a matter of adding the is function or method to your error type it needs to be of this signature it takes an error as an input and it returns a Boolean and then you can put whatever arbitrary logic you want inside of that method to check whether or not your instance of that error matches uh the error that's passed in as the target I'm not going to go into more detail on that because you can get as as arbitrarily complex as you want in there and for most cases it's really not necessary and that leaves errors dot as so let's talk about that one as I mentioned a moment ago um you'll likely use errors.as more often than errors.is although it depends on your uh on your use case both are definitely useful um errors.az is in particular more useful if you use error interface types which I tend to prefer which I may talk about in a future video but let's look at how errors.az works all right so once again I've modified my Foo function this time I'm calling json.unmarshall I'm passing some invalid Json input and I'm passing a nil argument to it so this will not work at all and that's my intention I wanted to return an error let's execute the code and see what it does and indeed I do get an uh an error so the original error that we wrap from the Jason on marshaller says invalid character I looking for beginning of value and then we wrap that with boo had an error as you can see in the output here so how would we use errors.as here well the encoding Json package in the standard library has a number of types of errors that it can return so let's let's pick one so we're trying to unmarshall so one of these unmarshall errors probably makes sense let's look at unmarshall type error so you can see here's an error type um that has a number of fields in it and it it matches our error interface you can see here it has the error method that returns a string uh so I don't know if this is the type of error that I'm returning uh in uh with my convoluted error but but we'll find out so here we are back at the main function and I have replaced errors.is with errors.as now the way errors.az works is I need to I don't pass in just an arbitrary uh error for equality I pass in sort of a placeholder error value and if the original error can be represented or one of its wrapped components can be represented as the target type then it is assigned to that type that might be confusing let's see what it looks like here so I've created this variable called Json err and it is a pointer to a Json Marshall type error then I call errors dot as the first argument is err the value returned from the function and the second value is a pointer 2 by Target which is this Json err value if it is possible to convert err or one of its children to the Json err value then errors.az returns true and the Json err value will be set to that error value if it is not possible then errors.as returns false and this statement will not execute so honestly I don't know if this type matches wait so this may work it may not and if it doesn't we'll try another type let's see what happens it does not match so what that means is that Json and Marshall is returning an error of a different type let's check one of the next ones I'm really sure it's not this one it's probably unsupported value error so let's give it a try and see what we get let's say that and it is not that one there's really a simpler way to do this um oh I bet it's a syntax error actually let's let's try this there is a simpler way and I'll show you that in just a moment yes oh I okay this is syntax error I should update my message as well so there you can see errors.as in use now normally of course you would do more than just check whether or not it's that type although it's certainly useful for that but then you can actually use the uh the target value to access say struct members let me demonstrate that before I move on foreign so here I've updated my example to call the offset or to reference the offset uh field of the syntax error struct if you'll recall from the documentation that's actually the only publicly or exported member of that struct so let's rerun the example and see what that looks like so now the output shows syntax error at 1 which is the offset value there as I mentioned a bit earlier you can also use interface types here so rather than setting Json error to a pointer to a struct type it could be for example an interface and then within the conditional when errors.s returns true then the value I have represents an interface representation of the error or one of its children so let's recap the standard library's errors package gives us only four functions new to create new errors and then unwrap as and is to sort of recursively unwrap errors that may have been wrapped of course this leaves the question how do you wrap errors I mentioned that early in the video uh with my demonstration I used fmt.error F my other video on the topic of error wrapping goes into more detail on that and of course you can also create your own error wrappers if you wish and I will get into more details on that in an upcoming video if you're not already subscribed be sure to subscribe so you know when that video comes out if you've learned something about errors today please hit the like button and if you have any questions that I left unanswered uh leave a comment below I'd love to hear from you and I'll try to address those questions in an upcoming video until then boldly go [Music]
Info
Channel: Boldly Go
Views: 2,691
Rating: undefined out of 5
Keywords:
Id: 5DUuyWxXnC0
Channel Id: undefined
Length: 16min 14sec (974 seconds)
Published: Mon Nov 21 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.