The secret to making Golang error handling a breeze

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey I'm going to show you how error handling Works in go if you have come from a different programming language you know probably one of the things that will rub you the wrong way is how error handling works and go but I'm going to show you that it works and it's just a little bit different than what you're used to and if you follow the right idioms it can work really well I think you'll find that go programmers really enjoy the way error handling works and go even though it's different than what you might find in Java or c-sharp or anywhere with exception handling so this article written by my colleague Brandon also a fellow Ontario and terrific let's go through it so in go an error is anything that implements this interface it's a very simple interface right all you have to do to implement this interface is have an error method that returns a string that's it so if you do that you're set the the simplest way to construct errors you don't even Implement that interface yourself all you do is import this errors package and then do errors.new and give it the string and then that's going to return an error something that you know when you call error on it we'll return the string that you passed in something didn't work simple enough the thing you should realize right is these are errors they're just normal types that Implement that interface they're not exceptions you don't throw them here's some code you return an error just like you return anything else there's nothing particularly magic about how errors work and go it follows the the same format as every everything else so here I have this method divide and it takes in two integers A and B and then since this can fail if I try to divide by zero there's an error case I need to return the result but also return an error if something goes wrong there's no sum types in in go which I would enjoy so you always are returning both of these and then the person who calls it needs to check whether there is an error and this actually works surprisingly well once you get used to it so here's the implementation right I am going to check if B equals zero and if so when I return this pair I'm going to return the zero value for whatever this type is which in this case is literally zero and then I'm going to return my error the way I'm returning an error right here is I've imported this fmt package which is format and then I have formatted an error message to return right using this D for numbers which will insert my a value so it'll say like hey you can't divide three by zero that will be the returned message if I pass in three zero if I pass in three one it's going to return three or one and return a nil error message nil is the zero value of our error type and so you just have to track this as you call things if it can return an error then you need to handle it so this is a very common way to return errors and we're going to show you uh later on spoiler alert how you can use this to wrap errors and build up something similar to a stack Trace but as Brandon explains in this when you are dealing with nil errors what you need to do is in your calling code check if the error is nil and that kind of gives you a try catch type functionality except it's only at the next level up you have to explicitly handle that you may be thinking well what if there's a bunch of different types of errors that get returned like you know this is the simplest code possible but there could be eight different paths and eight different ways things go wrong and how when I call this do I figure out which one is the one that went wrong if all I have is a string I'm not gonna like pull this apart and here it's even a dynamic string I'm not gonna like regex does it start with can divide and say if it's a can't divide error that doesn't make any sense better way right the simplest way to deal with that is to to just declare the variables explicit types that you want to use so here all I'm doing is I'm giving a name to my error error divided by zero I still use the same like errors.new to set a string and then in my divide method I just return that explicitly which makes it easy in my code oh yeah here is an example of calling this by the way so I have my two variables and then I'm going to call divide right with A and B I'm going to get two return results back right this one being the error I'm going to check if the error is null and then I want to see what type of error it is right and here's where I use error is which lets me check if the error is equal to that type or if it's wrapped if that type wraps it which we'll get to later so this lets us pull it apart right so now we're getting kind of this field that I'm used to from one I learned Java and University and it's like try and then catch an i o exception and catch a DNS exception catch a general exception right we can check the various types that are returned this way so that is one way right to be explicit as a type of error this is what Brandon and I guess anyone would call a sentinel it's a specific value marking something it's a sentinel error you can do more than that though right you can do a custom error type in this specific case we're just dividing by zero it's not the most involved error case but let's pretend that we want to carry along more information with our error right so what we're going to do is we're going to make our own type called division error and in it we're going to store a we're going to store B and we're going to store our message and then to make this into an error right we have to implement that error interface so this is how we do that we're going to say return error message for division errors right so now division error is an error it implements this interface now in our divide rather than returning that specific Sentinel value which you know we can't store values in it's just a global Singleton we can create a new division error set into it our formatted message to say whatever we want as well as set our end a and entity which means when we get down here here's my main method right I'm doing all the same stuff as before but now I'm going to use error.as so error.az we've declared a division error and then we're going to use air. as to say hey can the error returned be treated as a division error and it'll say oh yes it can this will return true it will set this value and then now that we have this value set from these values returned up here we can do whatever we want with them right we can pull apart into a and to be use the messages and so this is a way to kind of have more information in our errors that we want to Bubble Up and of course if it's not that case we're just going to handle that here I mean in this case in all of these there's not really much else that could go wrong here but in actual code you probably have a number of code paths and maybe there's just one specific case you want to handle specifically and then the other case is you want to handle more generically and so it does make sense to have a default right to cover all your options this works great if we need more specific information but there is a case we're not covering let me show you let's say we have database we have users in it and we have some code that deals with that right so we have this user struct and we have these methods find user right and then set user age and you can see from the return type find user will return a user but it can also return an error if something goes wrong set user age you know we pass it a user and an age and it's going to try to make a database update right but that can also error out okay this is fine here's where it's not fine we build up some more complicated code right so here's our implementation of find user our implementation of set user and then we have find and set user age which calls find user calls set user and then if everything's all right then it just returns nil there's no error right we're calling this for the side effects right so there's no error that's good if something does go wrong either in find user or set user we're going to return an error here's why this isn't perfect when we call find user uh sorry find and set user age with Bob and 21. either of these could return an error and we don't have a way to tell which one's which right like if this returns a DB error and this returns a DB error we can't tell if the error came from set user age or from find user the the return is the same and it could be that these both return a connection string not found error right it could be the same error we need a way to tell where it came from we we need to kind of be able to tell the type but also the path right that this error came from something like a stack Trace that you might expect if you were doing a throw and wrapping errors is the way that we do that so let's improve upon this code right I mean this works but debugging not ideal so that's why as Brandon outlines in this article which you should read and go 1.13 they added wrapped errors so Raptors still use this method we've been using this format dot error F but we can use the verb W to wrap and when we use w we have to pass into a an error we're making a new error wrapping the old error kind of building up a stack Trace but instead it's a nested group of errors as we explicitly handle our exceptions up to the call stack let me show you what I mean so here's our find user again it can return an error and when we get it an error from it we're not going to just return it or return our own new error instead we're going to wrap the existing error so we're going to add this new message find user fail executing query and then we're going to add W here which is going to have the result of making a string that combines both of these but in implementation Wise It's actually nesting one inside the other which you'll see in a moment same for set user age right we're going to do the same trick here where we pass the error in and then we can even do it at this level find and set user age we're going to wrap the error here right so you can see we're explicitly handling the error at each level and kind of adding a message that's more specific about what happened and that means when we come down here and we actually you know run this we get an error back we'll get an error message that looks like this so we'll get this failed finding or updating user right which comes from here and then we'll get find and set user age which comes from here and then set user age so that tells us we're here so we can now see the problem happened here right not here and so that's just reading the text message just reading the text the string that's returned by an error lets us tell where uh you know the the path that the air bubbled up from but it's not just string concatenation that's happening here it is literally wrapping one error in the other and the way that we can tell that is this error is I don't think he has an example of it but let me show you so here when we say error is error divide by zero this isn't only checking for direct identity it will also return true if this error here contains wrapped inside of it the error divide by zero which means that each of these levels when we do a wrap we're not losing this information that the original error was error divided by zero we can still pull that apart even though it's several levels down the error wrapping return stack and it's the same for the air as when we do error as here it's not just the current error but if we had wrapped it that would also work as well so that kind of lets us unwrap those errors and therefore pull things apart the same way you might expect in some place with the try catch just a little bit of a different and more explicit mechanism yeah so that's wrapping errors and you can see like I'm I'm not a go Guru I think the complaint that people have about this error checking mechanism is that there's a lot of this right if Air does not equal to nil return this right a lot of explicit checking and wrapping but the beauty part of it is that is very explicit right and it's very simple there's there's nothing magic here an error type is just any type that implements that interface there's nothing magical it's returned just like anything else and we just have some idioms you know some ways that we do things as as go programmers as developers to to handle these things but there's no magic right we can just explicitly read through and understand what's happening here this can return an error and when it does you know we'll check for it here and then we'll return our own error which we just append this message on the nice thing about the way that this works is it's very explicit when I was a C sharp developer you could you know throw an exception three levels down and catch it five levels up and and that was handy for a lot of cases but if you're looking at some of that code in the middle it's not absolutely clear that there are these errors that could be you know bubbling up from any point jumping right by all this code sure it's nice to have a stack Trace but this explicit way that it is done in go actually works pretty well and I think it's something you have to give it a try get a feel for how it works yeah so that is how you do error handling and go if you explicitly return it and wrap it and it's a little bit more typing but I think you'll find it works pretty well also if you want to learn more about go you should check out some of the other videos I made there's one about using make files some great videos about python too but yeah I think you might like some of the go ones that I've created and if you like this video check out Earthly this is the Earthly website we're looking at right here Earthly helps make your builds simpler it works great with go it's great for abstracting system level dependencies it's like Docker but for builds and I think if you like this video you'd really like it so so check it out thank you
Info
Channel: Earthly
Views: 9,091
Rating: undefined out of 5
Keywords: golang tutorial, go error handling, go errors, go error package, golang, error handling, go programming tutorial, go programming language, golang errors package
Id: d2DySHJ7oVk
Channel Id: undefined
Length: 13min 45sec (825 seconds)
Published: Mon Oct 02 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.