Rust Error Handling - Best Practices

แชร์
ฝัง
  • เผยแพร่เมื่อ 20 พ.ย. 2024

ความคิดเห็น • 72

  • @yudhiesh1997
    @yudhiesh1997 7 หลายเดือนก่อน +36

    More videos on best practices in Rust please! Perhaps some on Tests, Tracing, Scaling Rust Backends, etc.

  • @Aucacoyan
    @Aucacoyan 7 หลายเดือนก่อน +40

    Best Practices? yes please!

  • @dannelalbert7111
    @dannelalbert7111 7 หลายเดือนก่อน +12

    This is by far the best explanation of how to do robust error handling in Rust. I've always been a bit lost without anyhow and thiserr. This seems like a fantastic workflow that checks all the boxes I have been looking for progressing from test to production code without getting bogged down. Thank you for sharing this!

  • @Maximus98245
    @Maximus98245 3 หลายเดือนก่อน

    Great video and very well presented! Being a beginner in Rust, I kind of was lost after the first 5 minutes but I understood enough that I need to dig deeper into error handling, it's not just throw in "anyhow" or "thiserror" crate and you are good to go. I will keep coming back to this as I learn! Thanks again!

  • @norminemralino2260
    @norminemralino2260 7 หลายเดือนก่อน +4

    Another great video. I’ve noticed Jeremy talking about error handling in his last few videos, so it’s nice to see him do a video on error handling specifically. Though I think it could use a part 2. Maybe go ahead and implement structured Error type and add an example of a custom display for it and an example of calling the error. Maybe also add a error from a different module (he kinda talked about it)

  • @jamesray1820
    @jamesray1820 7 หลายเดือนก่อน +2

    Great, video I have been using these patterns in part since I started watching your videos. It is great to have it completely explained. Thank you!

  • @arcaneminded
    @arcaneminded 7 หลายเดือนก่อน +3

    Thanks mate. Small nit-pick you mentioned using embedded 'one day', but if you're using embedded, I'd really try to stay away from using trait objects and use the anyhow crate. Also the pass through `?` get's a little more complicated when dealing with embedded.

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน +3

      Ho, good points. I can understand the confusion. I meant just the type alias of Result, not the Box part for the error. Also, the whole ? and error handling seems to be a different beast in embedded, so the comment was a little misleading, I agree.
      I should have removed this comment, and just said, "same as std, and I like using core when possible as it lets me know what is not dependent on std."

  • @gbinux
    @gbinux 3 หลายเดือนก่อน +2

    Great video! May I ask how you would incorporate backtrace into the error definition?

  • @yelan5034
    @yelan5034 5 หลายเดือนก่อน +1

    Awesome, I was struggling with how to handle error gracefully, and you help me out a lot

  • @gabrielkwong1878
    @gabrielkwong1878 7 หลายเดือนก่อน +2

    Amazing video! More best practices videos please and thank you very much!

  • @SniffleSneeze
    @SniffleSneeze 6 หลายเดือนก่อน +1

    this is amazing ! thanks so much for taking the time to release such a good quality content.

  • @DASPRiD
    @DASPRiD 23 วันที่ผ่านมา

    So what do you think about thiserror instead of derive_from?

  • @dkoleary88
    @dkoleary88 5 หลายเดือนก่อน

    This is awesome. Thanks for making these videos. Very clear, concise and easy to understand and the actual content is amazing.

    • @JeremyChone
      @JeremyChone  5 หลายเดือนก่อน +1

      Thanks for the feedback.

  • @maxali6591
    @maxali6591 7 หลายเดือนก่อน +2

    Cool I was looking a video in that way.
    Thank you!

  • @Middlew
    @Middlew หลายเดือนก่อน

    Hi Jeremy, love your videos. I have some problems where I have two structures that is communicating with each other. A battle prompts the user for an action, and the user ai uses the context of a battle to determine an action. Sometimes, the user might request something from the battle that might fail. However, implementing from for both error types results in the infinite recursion error (obviously). Do you have a way you prefered to solve this problem? Some type or pointer or avoid the situation altogether? Thanks again for the video and keep up the good work!

    • @JeremyChone
      @JeremyChone  หลายเดือนก่อน +1

      @@Middlew can you show me some pseudo code to illustrate the issue. We can write From A for B, and From B for A. So, I think you are referring to something else.

    • @Middlew
      @Middlew หลายเดือนก่อน

      ​/error[dot]rs in battle folder:
      pub enum Error {
      ...
      #[from]
      User(error::User),
      ...
      }
      /error[dot]rs in user folder
      pub enum Error {
      ...
      #[from]
      Battle(error::Battle),
      ...
      }
      I used something similar in your axum course if I remember correctly, where you converted from different error types. That might have been for a better reason though, I dont quite remember.

    • @Middlew
      @Middlew หลายเดือนก่อน

      @@JeremyChone Hi sorry, I'm not sure you got my previous message so I'm trying again 😅

  • @froop2393
    @froop2393 6 หลายเดือนก่อน +1

    great! I like it.
    I have one question:
    You mentioned not to use an error module. But then you created one and did a (reexport?) to get rid of the error::Error import.
    Is that what you suggest as best practise?

    • @JeremyChone
      @JeremyChone  6 หลายเดือนก่อน

      Good point. I noticed after rewatching the video that this could be misleading.
      What I meant was that I do not expose my module Error within a submodule "error". However, for code organization, I put the code in error.rs and then re-export it at the root of the module. I should have clarified that. I showed it, but did not clarify it.

  • @Jubijub
    @Jubijub 7 หลายเดือนก่อน +2

    Thank you so much for this video, this is so useful. Beginner content is nice, but the examples are too trivial, and in particular don't cover well how to organize the code. Those are really neat patterns to know. More content like this please !

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน

      Thanks.
      You can find some examples of this usage in other videos/codebases. For instance:
      AI Function Calling: th-cam.com/video/2M0PSijLnis/w-d-xo.html
      Rust AI Buddy: th-cam.com/video/PHbCmIckV20/w-d-xo.html
      And the big Rust-Web-App blueprint github.com/rust10x/rust-web-app

  • @indramal
    @indramal หลายเดือนก่อน

    Hi, Thank you for your videos. I have question, why we need to do error handling like this? I am new to Rust. Any reasons? I mean last rust.rs file separation part.

  • @guacharo.w3871
    @guacharo.w3871 6 หลายเดือนก่อน +2

    thiserror crate is very usable for a prod too
    Btw great video!

    • @JeremyChone
      @JeremyChone  6 หลายเดือนก่อน +2

      Yes, if your product requires "English" display for your errors, this error is okay.
      However, I think that "English" display is useful only if it is intended to be shown to the end user. If it is just for developers and IT people, then the debug serialization as display is sufficient in most cases.
      This is why I like derive_more. It's relatively equivalent in result but we can choose what we want.

  • @juliuso.1183
    @juliuso.1183 6 หลายเดือนก่อน

    Hey Jeremy
    Thanks for the great series! I am learning a lot that I am trying to incorporate into our production server designs..
    Is there a reason as to why you use `impl std::fmt::Display` instead of `impl ToString` for custom error messages?

    • @JeremyChone
      @JeremyChone  6 หลายเดือนก่อน +1

      We could use `impl ToString`, but it is a little less flexible and less common/idiomatic than `impl Display`.
      For example, with `impl ToString` for `format!`/`println!`, we have to call `to_string()`, which requires adding them as an "argument," whereas with `impl Display`, we can just inline them in `{...}`.
      Also, typically, if we want a type to have `to_string()`/`ToString`, we would `impl Display for MyType` rather than implementing `ToString`. There's a blanket implementation of `ToString` for all types that implement `Display`, so we're good.
      Now, the other option that might be used is `impl Into`, which is more suitable for cases where the function needs to own the `String` but wants to give the caller the option to pass an owned `String` to avoid a new allocation if possible. If the caller passes a reference to a `String` or `str`, `into()` will allocate a new `String`.

  • @jensen7141
    @jensen7141 6 หลายเดือนก่อน +1

    High quality content! Thank you

  • @MrBenoitL
    @MrBenoitL 4 หลายเดือนก่อน

    Thanks for the video, it was very informative.
    I am quite new to Rust and struggle to write tests to see if a function returns the correct error (with the correct data inside) ?
    Rust is asking me to implement PartialEq for std::io::Error if write assert!(some function == Err(Error::Custom("some error"));
    what am I missing ?

  • @addDexter
    @addDexter 7 หลายเดือนก่อน +2

    Jeremy Chone have spoken, Lissan al Caib !

  • @TheZdannar
    @TheZdannar 7 หลายเดือนก่อน +1

    Great video. Nice pattern.

  • @pkozelka
    @pkozelka 7 หลายเดือนก่อน +1

    very nice, will try

  • @cariyaputta
    @cariyaputta 7 หลายเดือนก่อน +2

    Thanks.

  • @moquette31
    @moquette31 5 หลายเดือนก่อน

    in case you want to do a [no_std] library, when you "impl std::error:Error for Error {}" you don't have access to std, and core::error::Error is unstable. How do you handle this case ?

  • @TheOmfg02
    @TheOmfg02 7 หลายเดือนก่อน +2

    Great video!

  • @LucasOe
    @LucasOe 7 หลายเดือนก่อน +1

    For one off errors, do you create a new error variant, or do you just use a generic msg error with a string?

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน +1

      Depends if I am early in my code. If I am, then I use the Custom variant and the from static str, or formatted string.
      Then, when the code matures, I remove the Custom and all become variants, each with a descriptive variant name.
      I first focus on being descriptive in my variant names, and then work on consistency as the code design matures. Variant naming refactoring are simple and fast.

  • @Boronesss
    @Boronesss 7 หลายเดือนก่อน +1

    happy coding!

  • @irlshrek
    @irlshrek 7 หลายเดือนก่อน +1

    love this!!

  • @mykhailohnap
    @mykhailohnap 3 หลายเดือนก่อน

    If one test has a couple of places where the identical error could be returned, how do you know which place has failed? It seems that `unwrap` or `anyhow` could be better because they would allow us to see the exact place (line with `unwrap` and whole backtrace with `anyhow`) where the error occurred, no?

    • @JeremyChone
      @JeremyChone  3 หลายเดือนก่อน +1

      First, I would not use `unwrap` because of its backtrace support. It can make the code brittle. There are a few cases where `unwrap` is acceptable (e.g., when failing early is a must), but even then, it can be avoided for the sake of consistency.
      Regarding `anyhow`, yes, the `anyhow` macros do support backtraces, but I would avoid using the "anyhow magic" in my app or library errors, as it introduces another way to handle errors in Rust.
      If backtrace support is necessary, creating a custom macro, similar to `anyhow`, but using normal errors, might be a good approach.
      I haven't looked into this deeply, as it hasn't been a bottleneck in my code, and for me, the net value of having something cleaner and closer to pure return and error enums is higher.
      However, there is no absolute truth. Some teams might require backtraces, and in that case, you have to weigh both options.
      Either way, I would not recommend using `unwrap` in production code.

  • @adamsmith7630
    @adamsmith7630 7 หลายเดือนก่อน

    I don't think I understand why one might prefer writing your own boilerplate for your Error enum rather than using thiserror. Is it just to have finer control of the Display implementation?

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน +1

      Yes, thiserror takes over the display, which in my approach I don’t typically need as Debug as Display is plenty enough.
      With derive_more, I can still remove the From and Dispatch boilerplate, but I have the choice.
      About the display annotation:
      Annotating each variant with display is redundant and can lead to the anti-pattern of capturing meaning as text rather than having fully descriptive variant names and structures.
      For simple command-line applications, it’s fine, and the derive_more display removes the boilerplate like this error.
      For web or even desktop apps, typically we want to move the structure of the error to another system (e.g., log service/system) and have it displayed to the end user. For this, serializing to JSON is often a great approach.

  • @sunofabeach9424
    @sunofabeach9424 7 หลายเดือนก่อน +1

    how do you briefly display types on 10:39?

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน

      I use Toggle VSCode extension, and then add a key binding to toggle the inlay.
      You can find more info there: rust10x.com/vscode#keybinding-for-toggling-the-inlays
      (Note: this does not require the Rust10x vscode extension)

    • @sunofabeach9424
      @sunofabeach9424 7 หลายเดือนก่อน +1

      @@JeremyChone thnaks

  • @June-c2q
    @June-c2q 5 หลายเดือนก่อน

    This was great

  • @floristrading8418
    @floristrading8418 7 หลายเดือนก่อน +1

    had my suvbscribe after 1:25

  • @CuriousSpy
    @CuriousSpy 7 หลายเดือนก่อน +2

    Error::LimitTooHigh(LimitDescription)

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน +1

      I would use that pattern only if LimitDescription is used in other variants. Otherwise, I inline the struct members as variant struct members. It avoids unnecessary type proliferation.
      However, this could be considered a personal preference. Both are valid approaches.

    • @CuriousSpy
      @CuriousSpy 7 หลายเดือนก่อน +2

      @@JeremyChone newtype is also good when you need to generate code for other platforms, like graphql or typescript

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน +1

      Ha, good points. If the error type needs to be expressed in a type system that does not support algebraic types, then the decoupling might help. And, even then, we still have the different variant inner types to manage (albeit, union types on named types are easier to read than inline object types).
      For TS, I tend to serialize my Rust enum as "name/detail" in JSON, and I need to check if the toJSON schema can inline the type so that the JSON schema to TS type can express the correct structure.
      However, I typically concentrate on making the Rust part as clean and idiomatic as possible, and treat those external parts as "followers," and I am willing to make some concessions for them. But different approaches are valid as well.

  • @ИванРагозин-я8я
    @ИванРагозин-я8я 6 หลายเดือนก่อน

    Why didn't you show error creation for fs. What will you call the enum ? ErrorFs ?

    • @JeremyChone
      @JeremyChone  6 หลายเดือนก่อน

      Ah, it depends. I do not include error types for all submodules, focusing more on the main submodules that are likely need to have their own Error.
      This was just an example, but if I decided to have `crate::fs_utils` (I would rename it to avoid confusion with `std::fs`), I would do something like:
      `src/fs_utils/error.rs` to define `Error` with the `Result` type alias.
      And in `src/fs_utils/mod.rs` I would do:
      ```rust
      mod error;
      pub use self::error::{Error, Result};
      ```
      This way, if I am outside of the `fs_utils` module, I would use `Result` or `Error` as `fs_utils::Result` or `fs_utils::Error::...`.
      If I am within the `fs_utils` module, I would import and use `Result` or `Error` as needed.

    • @ИванРагозин-я8я
      @ИванРагозин-я8я 6 หลายเดือนก่อน +1

      @JeremyChone thanks so much for the detailed reply)

    • @ИванРагозин-я8я
      @ИванРагозин-я8я 6 หลายเดือนก่อน +1

      @JeremyChone please record a video: best practices for structuring a project on Rust. It's very hard to decide what names to give folders/modules for certain features. Thanks.

    • @JeremyChone
      @JeremyChone  6 หลายเดือนก่อน

      @@ИванРагозин-я8я Yes, there isn't a hard rule about when it's a good time to create an error for a module. However, there are some rules of thumb, so I need to articulate those.
      Also, these rules are okay to be broken once in a while. Additionally, some can be considered personal preferences.
      One video I want to make is "Module Best Practices," and in this video, we can definitely tackle this subject.

  • @betterinbooks
    @betterinbooks 7 หลายเดือนก่อน

    thank you

  • @NOISCALE
    @NOISCALE 6 หลายเดือนก่อน

    ❤❤❤

  • @MrHirenP
    @MrHirenP 7 หลายเดือนก่อน

    Hi! Is #[derive(From)] external crate proc macro? Or something to enable in std library?

    • @MrHirenP
      @MrHirenP 7 หลายเดือนก่อน +1

      Ok it’s from derive_more. Thanks!

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน

      Yes, as @MrHirenP said, it's part of the derive_more crate, which offers some useful procedural macros. I like it because it provides the trivial impl From (similar to the thiserror transparent), but separates it from the Display implementations (which thiserror takes over).

  • @rougegorge3192
    @rougegorge3192 6 หลายเดือนก่อน

    Fait tes tutos en francais la vague rust arrive dans le main stream

    • @JeremyChone
      @JeremyChone  6 หลายเดือนก่อน

      Thanks, could be a good idea. But this would probably need to be in another channel now and quite a bit of work to do both.

  • @kuqmua755
    @kuqmua755 7 หลายเดือนก่อน

    Really hate Result type alias concept

    • @JeremyChone
      @JeremyChone  7 หลายเดือนก่อน +2

      I grew to really like it.

    • @nubunto
      @nubunto 7 หลายเดือนก่อน +1

      Why tho, genuinely interested in your thoughts