Unpopular opinion: Go error handling is the best error handling method I've worked with. I so much prefer the consistency of writing 'if err != nil' to try catch statements, and having basically every function that can fail return an error variable makes it super clear where you handle which error. It makes for very human-readable code IMO.
@@giapvu2575 I still haven't made up my mind about error handling in Rust. On the one hand the '?' operator is glorious, but returning the error out of the function is often not what you actually want to do. Often you want to just handle the error. It's complicated, but perhaps that's because error handling is complicated.
IMO errors as types is even better. When you have a result, you HAVE to handle the error at that level, or return a result yourself. This means that you have to handle the error at some point before using the value in it, but you are flexible in where you actually handle it.
Same as Java then. You are forced to handle or rethrow checked exception. And unchecked exceptions in Java is like panic in go, no requirements to handle them
@@RidwanMarian Not quite the same. Checked expressions can jump over an intermediary function afaict, where with errors as types, you see the potential error at every level until the level where it is handled
Great video! I loved the part about wrapped errors. I was also thinking they're not that useful but your example definitely changed my mind. Please make more!
So much work just to re-invent nested exceptions. And you still end up with all the burden at every one of your call sites, unlike exceptions. Not sure why Go insisted on going back to C-style error handling patterns when better solutions were already established at the time of Go's creation.
I really wanted to say that if only go support something like if err ! = nil do return err so it could support one line. Then I realize I could just do if err ! = nil { return err }.
It'd be cool if you could just write "if err {", but I guess then we would need to introduce the concept of truthy and falsy in go and no sane person would want that I guess.
I’m very confused why you didn’t mention errors.join(….) At 12:29 you show your own implementation which is inferior and not standardised. But errors.join(…) does the exact same thing while also implementing the Unwrap interface which allows you to compare combined errors using errors.Is(…) to the individual errors. So errors.join(ErrA, ErrB) will compare as true with ErrA.
@@fullfungo Yes looking back I don't remember why I didn't bring up that the standard library had something like it already. I was looking for an example to demonstrate implementing a custom error in a more creative way, and that's what I picked from the open source code I had to choose from. In that project, I maintain go1.18 compatibility, so I do not have access to errors.Join in that project and I just made my own.
that's intentional because of the type system. go doesn't have sum types because is trying to do very little. there is some "just don't have skill issues" parts of go
@ItsNateY3K got a nil pointer as an unexpected argument? It shouldn't have been there in the first place and the log will show you where the thing came from
Go error handling is extremely simple, which follows the general philosophy of the language. It works perfectly fine for 95% of problems, and you benefit from a far simpler codebase and logic in your code. Rust wants to do everything optimally, which leads to far more complexity (especially when you bring async in) which doesn't benefit most problems. Go doesn't have an issue with tradeoffs, which makes it a better fit for most problems, but a terrible one sometimes, imo.
Not a Go programmer here: why don't you specify the actual type of the error in the return type? If you know the function returns a specific error type, you wouldn't need a type cast, right?
@@ianliu88 In my experience, the case where this is possible is considerably rare. For the cases where it is possible, the guidance from the Go team is not to because of what is described in this FAQ post: go.dev/doc/faq#nil_error
Level 6: "Is" receiver function and errors.Is with struct call to perform deep error comparison (e.g. that one of the wrapped errors matches UnsupportedOperationError{-1}) :)
This channel and video was sort of an experiment anyway, so when I started having a really busy 2024 I never tried making another. It was sitting at 100 views or so until last week, for some reason there's been a random uptick in views. I don't have access to my setup til next month, but I'll probably post some more once I do and see how it goes!
@@Djdje-p8r I could, though I worry it might be a bit of a dry video. Most of what's in this video is default VSCode functionality, with some stuff coming from the Go extension and VSCodeVim. Could maybe come up with a tips and tricks video though, I'll think about it!
I'm not go programmer, but... Am I correct: inside function body, in case of an error, you are forced to satisfy function signature? It may sound dumb - ofc you need to satisfy function signature. But. It looks SOOOOO strange to pack the error with some default value. What if return value type is something complex? You need to create some default value of complex type just to return it with error even if you know you want to panic? If it is as I understood - it's a garbage system. No offense to language or its users, but for me it sounds stupid. I heard go is extremely good language for new programmers, but I look at this error system and immediately think: "what if some newbie return some default value and error, but forget to check this error? He will be using default value. Even if he needs simple panic. That's soooo wrong." Moreover, even experienced programmers can make foolish mistakes, so this can happen to anyone. In my opinion, rust error system is just conceptually right if you dont use "?". Well, maybe if it's a pet project or a project is just small, you can use it and simple unwraps, but in real consistent project, I would never use "?" and "unwrap". Therefore, every error is handled before value is used in any way.
@@ЕвгенийКрасилов-о9о I only made this video for instructional purposes, not to make a stance on Go's error system vs Rust's. I don't have a moral stance on whether Go's language decisions were right or wrong. I can answer some of your Go questions though. > inside function body in case of an error you are forced to satisfy function signature? You are forced to satisfy the function signature at any time. In case of no error, you return the expected result and nil for the error value. In case of an error, you return whatever works (usually nil or some zero value) and then the actual error. > What if the return type is something complex I'm not sure of any such example of a complex return type in Go. You're either returning a primitive, a struct, an interface, or a pointer to something. Returning struct and primitive default values is really simple, and interfaces/pointers can be nil. I am not sure what you mean by "errors packed with some default value" here; error is an interface, so if there is no error you return nil. > What if some newbie return default value and error, but forget to check this error The language does not enforce error checking, so yes technically this could happen, but you'd have to willfully ignore it. Go will not compile with unused variables, so you'd have to take the error returned from a function as _. Most development environments will yell at you for this. Anecdotally, I have never seen someone doing this pass code review.
@@RageCageCodes-ik2ue thank you for the explanation! If it won't compile until I use (check) an error - it resolves my main issue with that. But I have one more question. You said "interfaces can be nil". So, for example, if a functions argument is some interface and I use this function with some struct that implements that interface, in case of error inside the function, I can return (nil, error)? Am I correct? If yes, ok, it's not bad at all, even good.
@@ЕвгенийКрасилов-о9о Yes that's right. If the return type is an interface/pointer and an error, then you can "return nil, err". By the same token, with the "error" type being an interface, you can "return value, nil" when there is no error. If the value is a struct as a value, let's say the struct type is called "X", then you can "return X{}, nil". Calling the struct initialization like that will just automatically make every field of the struct the zero-value of its type since Go doesn't have a concept of "uninitialized".
Dude made 1 legendary video and disappeared. What a lad.
Thanks for the kind words! I've been busy and have had trouble thinking of other video topics. I'll post again some day if I can think of something!
@@RageCageCodes-ik2ue I would appreciate videos where you show your style and workflow of coding. This is really helpful for beginners.
Video def helped. I don't code anymore on a day to day basis, especially with Go. But I use these videos to be able to easily read others code.
Unpopular opinion: Go error handling is the best error handling method I've worked with. I so much prefer the consistency of writing 'if err != nil' to try catch statements, and having basically every function that can fail return an error variable makes it super clear where you handle which error. It makes for very human-readable code IMO.
Try rust
@@giapvu2575 I still haven't made up my mind about error handling in Rust. On the one hand the '?' operator is glorious, but returning the error out of the function is often not what you actually want to do. Often you want to just handle the error. It's complicated, but perhaps that's because error handling is complicated.
IMO errors as types is even better. When you have a result, you HAVE to handle the error at that level, or return a result yourself. This means that you have to handle the error at some point before using the value in it, but you are flexible in where you actually handle it.
Same as Java then. You are forced to handle or rethrow checked exception. And unchecked exceptions in Java is like panic in go, no requirements to handle them
@@RidwanMarian Not quite the same. Checked expressions can jump over an intermediary function afaict, where with errors as types, you see the potential error at every level until the level where it is handled
Awesome video! I was blessed by the algorithm with a recommendation. Keep up the good work!
You should make more videos! You seem like someone who values correctness and conventions. Love it 👍🏻👍🏻👍🏻
incredibly concise and thorough overview of go errors! I’m a huge advocate for go’s errors as value. Throw in sum types and I’d be on cloud9
Came across this video from recommendation. I've just started learning Go so this is very useful ❤
Level -1: mixing up 'err != nil' and 'err == nil' because there's one character difference between them
This is why I try to avoid *!* even outside Go.
Golang needs an extension where you can just type
REEE err
and it just evaluates to the same thing as if err != nil
The more else statements code has, the probability of errors approach one.
😂
@@i-am-the-slime It's a thing. Google "cyclomatic complexity".
This is an amazing video
Thats a good vídeo, thank you for sharing
Great video! I loved the part about wrapped errors. I was also thinking they're not that useful but your example definitely changed my mind. Please make more!
RCC DROPPING A BANGER
So much work just to re-invent nested exceptions. And you still end up with all the burden at every one of your call sites, unlike exceptions. Not sure why Go insisted on going back to C-style error handling patterns when better solutions were already established at the time of Go's creation.
The best thing you can do with errors in 90% of the cases is:
- Originate errors defined in a constant
- Propagate errors using fmt.Errorf
Dude looks like he betrayed William Wallace
Woooo! Let's go Mr. Rage!🔥🔥
I learned so much! Thanks Mr Rage
in my channel have 5 levels for error handling, I want to know what the 5th level is
I really wanted to say that if only go support something like if err ! = nil do return err so it could support one line. Then I realize I could just do if err ! = nil { return err }.
It'd be cool if you could just write "if err {", but I guess then we would need to introduce the concept of truthy and falsy in go and no sane person would want that I guess.
I’m very confused why you didn’t mention errors.join(….)
At 12:29 you show your own implementation which is inferior and not standardised.
But errors.join(…) does the exact same thing while also implementing the Unwrap interface which allows you to compare combined errors using errors.Is(…) to the individual errors.
So errors.join(ErrA, ErrB) will compare as true with ErrA.
@@fullfungo Yes looking back I don't remember why I didn't bring up that the standard library had something like it already. I was looking for an example to demonstrate implementing a custom error in a more creative way, and that's what I picked from the open source code I had to choose from. In that project, I maintain go1.18 compatibility, so I do not have access to errors.Join in that project and I just made my own.
It's a great video. Do more
Thank you! I plan to
Wow this feels very brittle compared to Rust
Well, Go was born in 2009, it was the first hyped language to introduce errors as optional output. The idea was still emerging.
that's intentional because of the type system. go doesn't have sum types because is trying to do very little. there is some "just don't have skill issues" parts of go
@ItsNateY3K got a nil pointer as an unexpected argument? It shouldn't have been there in the first place and the log will show you where the thing came from
Go error handling is extremely simple, which follows the general philosophy of the language. It works perfectly fine for 95% of problems, and you benefit from a far simpler codebase and logic in your code. Rust wants to do everything optimally, which leads to far more complexity (especially when you bring async in) which doesn't benefit most problems. Go doesn't have an issue with tradeoffs, which makes it a better fit for most problems, but a terrible one sometimes, imo.
Just some ExceptT monad transformer in PureScript with polymorphic variants for the error types is the nicest.
nice video !
awesome video, keep posting
Not a Go programmer here: why don't you specify the actual type of the error in the return type? If you know the function returns a specific error type, you wouldn't need a type cast, right?
@@ianliu88 In my experience, the case where this is possible is considerably rare.
For the cases where it is possible, the guidance from the Go team is not to because of what is described in this FAQ post: go.dev/doc/faq#nil_error
bro write a function to handle this statement
Level 6: "Is" receiver function and errors.Is with struct call to perform deep error comparison (e.g. that one of the wrapped errors matches UnsupportedOperationError{-1}) :)
moreplatesmoredates?
I'm, generously, 1/10th as jacked as that guy
Why did you stop posting?
This channel and video was sort of an experiment anyway, so when I started having a really busy 2024 I never tried making another. It was sitting at 100 views or so until last week, for some reason there's been a random uptick in views. I don't have access to my setup til next month, but I'll probably post some more once I do and see how it goes!
@@RageCageCodes-ik2ue TH-cam recommended me this video like 5 times and I finally gave in. Great content, you should definitely do more!
@@RageCageCodes-ik2ue hello, what’s ur vscode setup? Mb u can make a video about it
@@Djdje-p8r I could, though I worry it might be a bit of a dry video. Most of what's in this video is default VSCode functionality, with some stuff coming from the Go extension and VSCodeVim.
Could maybe come up with a tips and tricks video though, I'll think about it!
Do more!!!!!!!!!!!
rust enums solves all of this
I'm not go programmer, but... Am I correct: inside function body, in case of an error, you are forced to satisfy function signature?
It may sound dumb - ofc you need to satisfy function signature. But. It looks SOOOOO strange to pack the error with some default value. What if return value type is something complex? You need to create some default value of complex type just to return it with error even if you know you want to panic?
If it is as I understood - it's a garbage system. No offense to language or its users, but for me it sounds stupid.
I heard go is extremely good language for new programmers, but I look at this error system and immediately think: "what if some newbie return some default value and error, but forget to check this error? He will be using default value. Even if he needs simple panic. That's soooo wrong."
Moreover, even experienced programmers can make foolish mistakes, so this can happen to anyone.
In my opinion, rust error system is just conceptually right if you dont use "?". Well, maybe if it's a pet project or a project is just small, you can use it and simple unwraps, but in real consistent project, I would never use "?" and "unwrap". Therefore, every error is handled before value is used in any way.
@@ЕвгенийКрасилов-о9о
I only made this video for instructional purposes, not to make a stance on Go's error system vs Rust's. I don't have a moral stance on whether Go's language decisions were right or wrong. I can answer some of your Go questions though.
> inside function body in case of an error you are forced to satisfy function signature?
You are forced to satisfy the function signature at any time. In case of no error, you return the expected result and nil for the error value. In case of an error, you return whatever works (usually nil or some zero value) and then the actual error.
> What if the return type is something complex
I'm not sure of any such example of a complex return type in Go. You're either returning a primitive, a struct, an interface, or a pointer to something. Returning struct and primitive default values is really simple, and interfaces/pointers can be nil. I am not sure what you mean by "errors packed with some default value" here; error is an interface, so if there is no error you return nil.
> What if some newbie return default value and error, but forget to check this error
The language does not enforce error checking, so yes technically this could happen, but you'd have to willfully ignore it. Go will not compile with unused variables, so you'd have to take the error returned from a function as _. Most development environments will yell at you for this. Anecdotally, I have never seen someone doing this pass code review.
@@RageCageCodes-ik2ue thank you for the explanation! If it won't compile until I use (check) an error - it resolves my main issue with that.
But I have one more question. You said "interfaces can be nil". So, for example, if a functions argument is some interface and I use this function with some struct that implements that interface, in case of error inside the function, I can return (nil, error)? Am I correct?
If yes, ok, it's not bad at all, even good.
@@ЕвгенийКрасилов-о9о Yes that's right. If the return type is an interface/pointer and an error, then you can "return nil, err". By the same token, with the "error" type being an interface, you can "return value, nil" when there is no error. If the value is a struct as a value, let's say the struct type is called "X", then you can "return X{}, nil". Calling the struct initialization like that will just automatically make every field of the struct the zero-value of its type since Go doesn't have a concept of "uninitialized".
Wow! I didn't know that Go has its own `object.has_method("method")` like in GDScript.