This video is superb - you’ve demonstrated the problem, and then the solution’s superiority. Almost a video which can serve as a good selling point for Rust!
Thanks Bogdan for showcasing the "error-stack" error handling crate/library. Very cool stuff. And I applaud you for the effort you went through to show how this can really be used to make a dev's life easier when debugging programs out in the wild! Just set the logging level accordingly and you'd get enough info to pin-point an a problematic code path/etc. 👍👍👍! Honestly, there is about 20+ software development TH-cam channels I have subscribed to, but yours is in the TOP 3 to watch for sure (no questions asked!)!!! Especially if one is interested or working with Rust. You are doing a banger of a job here! Thank you! 👍👍👍
@@lizzyfleckenstein9837 I meant the method names and code to setup the errors was verbose. The messages themselves are not important. I'm talking about ergonomics of the api.
While it is a nice API, it feels like a lot of effort for little reward. Consuming all this time using an API one has to learn, to eventually produce messages you could print directly. There has to be something made simple with little to remember and faster to write. We programmers understand stack errors. Why can't we just have a stack without fancy terms and methods/functions?
I would be interested in a comparison between error stack and miette. I kinda prefer the miette naming scheme and output. In general, "crate battle" would be a cool video format. Other ideas for this format: egui vs iced vs yew, bevy vs microquad,
this is an awesome video! I usually build huge enums but this approach looks awesome! perhaps it would be confusing to new developers reading the code and seeing this approach for the first time. but overall looks great
Not bad but all this inline error handling (actually, data checking in this case) makes the actual program logic rather unreadable. I still prefer the C-style "if check1_fails return; ...if check_n_fails return; ...do_the_work;" I know Rust enforces error checking, which is good, but it would be nice if all that checking could be moved up in the code.
Actually, every single "if check_fails { return }" is still there, condensed into a single "?" operator at the end of every function call. It's just that before we get to that point, we attach a lot more information to the error message. You can still do that style as shown at 13:18. However, there are some cases where you cannot simply return immediately as you error. Rust encourages generous use of iterators, which are functional in nature. This usually means there is work done within "closures"; anonymous functions. You have to handle errors within those closures, and only rely on the upper levels to add more context.
This is one of the important videos. Error handling is super important IMO, I prefer the clean and deep context of error log. Moreover it provides documentation as well! Thanks 😊
Thanks for the video and the effort of improvement made along different videos. I see you are now working on a mac and another editor. Which one is it?
Ooooohhhhhhhhh. @ 12:00 Now I see what they're going for. Thanks. I'd been curious about this library, but didn't quite grok what they were offering from a few skims. I'd be curious to hear how you think this compares to Miette -- which was around before, gathering error information into a Report type and sending forth. But with, it seems, more focus on user usable errors.
Another great video as always! Do you, by any chance, have any plans on making a video about Tauri framework?
3 หลายเดือนก่อน
Standard error reporting is better. By that I mean the traditional: "Error: Cause: Sub-cause: Sub-sub-cause: etc." Reporting the file + line is not good for user-final error reporting. Also, the report tree is also not suitable for user information, who would like something much more simple and direct. Also, if you need file + line information for every error, you're doing your error handling wrong. Rust "Result" error handling is supposed to be "final" and "complete". Where you need file + line info is in unexpected situations (panics), where you'll get that info. Just use traditional error handling coupled with 'thiserror' crate, that will help you achieve high level of completeness and accuracy. This also adds too much bloat to the code itself. Also, you need to worry about "am I actually handling errors, or am I just reporting them?". Using this or "anyhow", you might end up with a program that does not handle errors, just reports them. If only you developer use the program, it might be OK, but not for the general user.
Thank you for showing your face as you speak. When the topic gets complicated it helps me understand what you're saying when I can see your face and mouth move as you speak. Also, thank you for not speaking while you put code into the IDE. It is helpful to be able to focus on what is being written.
I'm struggling to understand the usefulness of the crate if we already have access to expect() and match error handling. Perhaps I'm missing something?
Perhaps it has to do with style of programming and problem-space. For the things I'm currently working on, it is useful to see the errors in context to each other. Expect is incredibly useful, but sometimes you want more information around it. I also would not introduce this crate in my project until it reached a point of complexity that leaves me wishing I had better ways to handle my errors, but this is just me. I can easily see the vast majority of projects doing just fine with only what std comes with by default.
So "expect()" just panics (calls the panic!() macro) with a custom error message. Very often, especially if you write library code that is consumed by others, or even when you write a simple webservice and you don't want to end up with a "500 internal server error" on the client/browser you will need to be able to customize your error handling and decide when and how to return a proper error, or - and this is the worst-case scenario - when you actually have to call panic!(). Calling panic!() is a "using a last resort"-kinda situation and should only be used very sparingly in my opinion. Ask yourself this: if you write a database server/service and a user sends an invalid query, do you panic!() and force a shutdown of the whole database or do you return a nicely formatted error message to the user telling them their query has an issue (and ideally what issue and where (and how to fix it, if possible))? Same thinking goes for any kind of web-service/web-app. Those examples I described above are usually multi-threaded, because they serve not just one single client at a time, but can serve multiple clients at the same time. Do you really want to have the whole service exit/go down just because one client is misbehaving? No, of course not. Never actually. Also, of course I am over-simplifying things here, even calls to "panic!()" in Rust can be "intercepted", kinda - it is a quite advanced feature to use for sure, so totally not beginner friendly and hence not widely advertised. Also it is certainly not as easy in Rust as it is in other programming languages who provide a more traditional exception-based error handling, that's for sure. So, in conclusion from my point of view: your argument is completely understandable from a beginners (or even intermediate) Rust programmer's point of view, so I am glad you asked. From what I understand so far (and I've only used Rust for the last 2~3 years), it is like I explained above (someone more experienced in Rust please correct me when I am wrong (not if, because I am most likely wrong with some detail or another). That being said: error handling in Rust is still one of, if not, _the_ language features I struggle with the most. The borrow checker is "easy mode" compared to this (for myself that is, _at this point in time_ 😊).
I kind of like it, because it creates printable messages in one "logical" line, that may or may not be printed/logged/displayed later, but at the same time this creates a problem if we want to localise the application later, because we haven't separated data from presentation. Can this approach be combined with "thiserror" to derive the Display and Error traits for our types instead of providing them manually?
Using only std, you can use Result::map_err to change the error type. You should also make all format! macros lazy. Else, the string will be created every time the function is called.
What font and what color scheme do you use? It looks different from what we have on Windows and Linux, but maybe it's because you're running VSCode on macOS.
Font: JetBrains Mono Color theme: Dark+ (available by default) Parentheses are colored in yellow/blue/purple too, thanks to the Bracket Pair Colorizer... which I just learned today has become a VSCode built-in. 7_7
Cool but cannot shake off the feeling this is somewhat overengineered. Would like to see some more lightweight approach. Anyhow and thiserror looks quite fine to me even though it's not as robust as this approach. Last detailed root cause for the error was excellent, though!
Awesome, but what would be easiest way to remove all lol logging from release build..? I mean it's perfect for debugging but what if we want to release the code?
Excellent tutorial! I just disagree on the user-facing 'Try again' message. There is nothing to try again, the program will consistently fail. I would even argue this is the most frustrating type of error message you can give to anyone, unless trying again actually has merit. In this case, a message like 'Entry for {name} was found, but is malformed and cannot be returned.' would be much more explicit and suitable. This is a message you can then pass back to the dev, or google for and get some proper fix for it.
I would like to learn rust, but reading the code with a lot of &&, ?, ::,! - I know this is a specific of the language and this is a basics everyone should know. But if you look at the python code even though you haven't met it before, you will understand what it does. In here I see a lot of code for error handling (switching context, attaching printable, etc.) In most cases all of this code should be a template, can we achieve the same result with less amount of code?
Where is the problem in returning an Box everywhere multiple Errors are thrown in one function? Also this version is very verbose in my opinion. I will use the normal error trait instead.
It's an incredible oversight (stupid decision) to not have exceptions in Rust and then having to develop stuff like this to emulate it in a _very_ cumbersome way.
Exceptions is a bad concept. Errors and mistakes are parts of life and expected to happen, there is nothing exceptional in them. You shoud treat errors just like another valid code branch in your program.
I don't like the "error-stack" crate approach at all. It mixes bunches of successful path execution statements with bunches of failure path execution statements (e.g., parse() with report() or collect() with change_context()) as opposed to factoring out error creation and transformation into separate blocks (From trait usage). This intermingled approach is used in reactive programming libraries such as OpenCombine and it gets so tricky that it can confuse the livin' sh*t out of everyone except for absolute experts. Rust's golden rules are simplicity, readability and predictability - and that probably ain't it.
Don't cut the corners with the simple yet incomplete solutions. For the libraries I recommend `thiserror` lib, and for the applications - `anyhow`, both are maintained by David Tolnay and extended by community. Also they are tested in production, so for me the choice for the error handling library is just no-brainer. 😉
📝Get your *FREE Rust cheat sheet* :
www.letsgetrusty.com/cheatsheet
This video is superb - you’ve demonstrated the problem, and then the solution’s superiority. Almost a video which can serve as a good selling point for Rust!
This problem simply solved with exceptions in other languages :)
But Rust's solution is better
@@vabka-7708 In other languages you can ignore exceptions and this can lead to a buggy code. Rust strives for correctness
@@warever9999 this is exactly what i said.
I'd love more videos on error handling, especially details on the structural problems the error group are solving.
whenever i try to give up on RUST, it seems you always know via your newsletter. Thanks for the encouragement and I'll work harder!
Thanks Bogdan for showcasing the "error-stack" error handling crate/library. Very cool stuff. And I applaud you for the effort you went through to show how this can really be used to make a dev's life easier when debugging programs out in the wild! Just set the logging level accordingly and you'd get enough info to pin-point an a problematic code path/etc. 👍👍👍!
Honestly, there is about 20+ software development TH-cam channels I have subscribed to, but yours is in the TOP 3 to watch for sure (no questions asked!)!!! Especially if one is interested or working with Rust. You are doing a banger of a job here! Thank you! 👍👍👍
What are the other two?
This looks compelling. I hope the ergonomics are improved a bit. It's a little verbose as is.
When you are debugging, you mostly want verbosity.
You just dont want to throw a lot of verbose messages at the user.
@@lizzyfleckenstein9837 I meant the method names and code to setup the errors was verbose. The messages themselves are not important. I'm talking about ergonomics of the api.
@@Codeaholic1 oh ok my bad
How to have a better day? It's Friday and a new LGR video 🎉
Seems like an awesome crate! Thanks for bringing it to our attention!
While it is a nice API, it feels like a lot of effort for little reward. Consuming all this time using an API one has to learn, to eventually produce messages you could print directly. There has to be something made simple with little to remember and faster to write. We programmers understand stack errors. Why can't we just have a stack without fancy terms and methods/functions?
You can make your own macro?
I would be interested in a comparison between error stack and miette. I kinda prefer the miette naming scheme and output.
In general, "crate battle" would be a cool video format.
Other ideas for this format:
egui vs iced vs yew,
bevy vs microquad,
It would be very educational to implement the same functionality with eyre, so the viewers can see the comparison of the two libraries.
this is an awesome video! I usually build huge enums but this approach looks awesome! perhaps it would be confusing to new developers reading the code and seeing this approach for the first time. but overall looks great
Not bad but all this inline error handling (actually, data checking in this case) makes the actual program logic rather unreadable. I still prefer the C-style "if check1_fails return; ...if check_n_fails return; ...do_the_work;"
I know Rust enforces error checking, which is good, but it would be nice if all that checking could be moved up in the code.
Actually, every single "if check_fails { return }" is still there, condensed into a single "?" operator at the end of every function call.
It's just that before we get to that point, we attach a lot more information to the error message.
You can still do that style as shown at 13:18.
However, there are some cases where you cannot simply return immediately as you error. Rust encourages generous use of iterators, which are functional in nature. This usually means there is work done within "closures"; anonymous functions. You have to handle errors within those closures, and only rely on the upper levels to add more context.
evry time i watch your vids like this, i get more hyped to learn rust!
Really nice, error handling in rust has been so tedious so far
This is one of the important videos. Error handling is super important IMO, I prefer the clean and deep context of error log. Moreover it provides documentation as well! Thanks 😊
Damn! That error handling is so cool. Can't wait to have it in stable
Thanks for the video and the effort of improvement made along different videos.
I see you are now working on a mac and another editor. Which one is it?
Nice video! i found this crate a little difficult, i think error handling should be as simple as posible and should not take to much time :/
Ooooohhhhhhhhh. @ 12:00 Now I see what they're going for. Thanks. I'd been curious about this library, but didn't quite grok what they were offering from a few skims. I'd be curious to hear how you think this compares to Miette -- which was around before, gathering error information into a Report type and sending forth. But with, it seems, more focus on user usable errors.
Another great video as always!
Do you, by any chance, have any plans on making a video about Tauri framework?
Standard error reporting is better. By that I mean the traditional: "Error: Cause: Sub-cause: Sub-sub-cause: etc."
Reporting the file + line is not good for user-final error reporting. Also, the report tree is also not suitable for user information, who would like something much more simple and direct. Also, if you need file + line information for every error, you're doing your error handling wrong. Rust "Result" error handling is supposed to be "final" and "complete". Where you need file + line info is in unexpected situations (panics), where you'll get that info. Just use traditional error handling coupled with 'thiserror' crate, that will help you achieve high level of completeness and accuracy.
This also adds too much bloat to the code itself.
Also, you need to worry about "am I actually handling errors, or am I just reporting them?". Using this or "anyhow", you might end up with a program that does not handle errors, just reports them. If only you developer use the program, it might be OK, but not for the general user.
@Let's get rusty, please, make video about Cow smart pointer
There is a good explanation of Cow at: th-cam.com/video/8O0Nt9qY_vo/w-d-xo.html
Thank you for showing your face as you speak. When the topic gets complicated it helps me understand what you're saying when I can see your face and mouth move as you speak.
Also, thank you for not speaking while you put code into the IDE. It is helpful to be able to focus on what is being written.
I'm struggling to understand the usefulness of the crate if we already have access to expect() and match error handling. Perhaps I'm missing something?
Perhaps it has to do with style of programming and problem-space. For the things I'm currently working on, it is useful to see the errors in context to each other. Expect is incredibly useful, but sometimes you want more information around it. I also would not introduce this crate in my project until it reached a point of complexity that leaves me wishing I had better ways to handle my errors, but this is just me. I can easily see the vast majority of projects doing just fine with only what std comes with by default.
So "expect()" just panics (calls the panic!() macro) with a custom error message. Very often, especially if you write library code that is consumed by others, or even when you write a simple webservice and you don't want to end up with a "500 internal server error" on the client/browser you will need to be able to customize your error handling and decide when and how to return a proper error, or - and this is the worst-case scenario - when you actually have to call panic!().
Calling panic!() is a "using a last resort"-kinda situation and should only be used very sparingly in my opinion.
Ask yourself this: if you write a database server/service and a user sends an invalid query, do you panic!() and force a shutdown of the whole database or do you return a nicely formatted error message to the user telling them their query has an issue (and ideally what issue and where (and how to fix it, if possible))? Same thinking goes for any kind of web-service/web-app.
Those examples I described above are usually multi-threaded, because they serve not just one single client at a time, but can serve multiple clients at the same time. Do you really want to have the whole service exit/go down just because one client is misbehaving? No, of course not. Never actually.
Also, of course I am over-simplifying things here, even calls to "panic!()" in Rust can be "intercepted", kinda - it is a quite advanced feature to use for sure, so totally not beginner friendly and hence not widely advertised. Also it is certainly not as easy in Rust as it is in other programming languages who provide a more traditional exception-based error handling, that's for sure.
So, in conclusion from my point of view: your argument is completely understandable from a beginners (or even intermediate) Rust programmer's point of view, so I am glad you asked. From what I understand so far (and I've only used Rust for the last 2~3 years), it is like I explained above (someone more experienced in Rust please correct me when I am wrong (not if, because I am most likely wrong with some detail or another). That being said: error handling in Rust is still one of, if not, _the_ language features I struggle with the most. The borrow checker is "easy mode" compared to this (for myself that is, _at this point in time_ 😊).
I kind of like it, because it creates printable messages in one "logical" line, that may or may not be printed/logged/displayed later, but at the same time this creates a problem if we want to localise the application later, because we haven't separated data from presentation.
Can this approach be combined with "thiserror" to derive the Display and Error traits for our types instead of providing them manually?
Using only std, you can use Result::map_err to change the error type.
You should also make all format! macros lazy. Else, the string will be created every time the function is called.
How to do format! lazy?
@@szymek1567 the lazy_format crate can do that.
He did it once at 10:17 (attach_printable_lazy).
What font and what color scheme do you use? It looks different from what we have on Windows and Linux, but maybe it's because you're running VSCode on macOS.
Font: JetBrains Mono
Color theme: Dark+ (available by default)
Parentheses are colored in yellow/blue/purple too, thanks to the Bracket Pair Colorizer... which I just learned today has become a VSCode built-in. 7_7
very nice video, error handling is always pain in the ass for me
Fantastic crate and video. Thank you sir.
Cool but cannot shake off the feeling this is somewhat overengineered. Would like to see some more lightweight approach. Anyhow and thiserror looks quite fine to me even though it's not as robust as this approach. Last detailed root cause for the error was excellent, though!
Anyhow/Eyre are the lighter weight options, if you aren't interested in the detailed context info.
Awesome, but what would be easiest way to remove all lol logging from release build..? I mean it's perfect for debugging but what if we want to release the code?
i think you can do something like this
#[cfg(debug_assertions)]
fn init_logging() { env_logger::init(); }
May I ask which is better use enum or struct? I found that use enum just need one line code to represent one error. Struct is too many lines of code
what is that extension that shows the potential errors in red on the same line? e.i. s.parse() and then there is type mismatch.... error showing
“Error Lens” extension in VSCode
@@joshuahewlett44 Great, thanks!
This video was great! Thanks for making it.
Excellent tutorial!
I just disagree on the user-facing 'Try again' message. There is nothing to try again, the program will consistently fail. I would even argue this is the most frustrating type of error message you can give to anyone, unless trying again actually has merit.
In this case, a message like 'Entry for {name} was found, but is malformed and cannot be returned.' would be much more explicit and suitable. This is a message you can then pass back to the dev, or google for and get some proper fix for it.
Damn, this crate is awesome
Thanks for your efforts,Bogdan
Thanks for your effort, Bogdan!
Looks useful. Thanks for sharing!
Thank you man ❤
What is an error context?
Thanks you a lot!! Very nice video!
Getting some bad C++ vibes here. Too overengineered.
How are you typing so fast?
Amazing! Congrats.
thank you 😊
I would like to learn rust, but reading the code with a lot of &&, ?, ::,! - I know this is a specific of the language and this is a basics everyone should know. But if you look at the python code even though you haven't met it before, you will understand what it does.
In here I see a lot of code for error handling (switching context, attaching printable, etc.) In most cases all of this code should be a template, can we achieve the same result with less amount of code?
Manually emulating exception handling....
Cool, but lost me at the Nightly requirement. Surely this can also be achieved using anyhow::Context??
This is dope
Where is the problem in returning an Box everywhere multiple Errors are thrown in one function? Also this version is very verbose in my opinion. I will use the normal error trait instead.
Wow
It would be so grate to use ai to generate error handling and testing for code...
As always, great video
But dude please get a sleep
bogdan tu e foda
*good video :)*
It's an incredible oversight (stupid decision) to not have exceptions in Rust and then having to develop stuff like this to emulate it in a _very_ cumbersome way.
Exceptions implies things happening without you knowing. Thats just not what rust is made for.
Exceptions is a bad concept.
Errors and mistakes are parts of life and expected to happen, there is nothing exceptional in them.
You shoud treat errors just like another valid code branch in your program.
I don't like the "error-stack" crate approach at all. It mixes bunches of successful path execution statements with bunches of failure path execution statements (e.g., parse() with report() or collect() with change_context()) as opposed to factoring out error creation and transformation into separate blocks (From trait usage). This intermingled approach is used in reactive programming libraries such as OpenCombine and it gets so tricky that it can confuse the livin' sh*t out of everyone except for absolute experts. Rust's golden rules are simplicity, readability and predictability - and that probably ain't it.
Don't cut the corners with the simple yet incomplete solutions. For the libraries I recommend `thiserror` lib, and for the applications - `anyhow`, both are maintained by David Tolnay and extended by community. Also they are tested in production, so for me the choice for the error handling library is just no-brainer. 😉
Error handling in rust is a nightmare
Gross I'd rather stay with Err.
too verbose. a lot of effort and reliance on an external dependency all over the app.
I don't care how fast it is rust is crap
Rust is the best programming language ever created, after Lisp and Haskell