Most active commenters
  • Cloudef(3)

←back to thread

611 points LorenDB | 11 comments | | HN request time: 0s | source | bottom
Show context
dvratil ◴[] No.43908097[source]
The one thing that sold me on Rust (going from C++) was that there is a single way errors are propagated: the Result type. No need to bother with exceptions, functions returning bool, functions returning 0 on success, functions returning 0 on error, functions returning -1 on error, functions returning negative errno on error, functions taking optional pointer to bool to indicate error (optionally), functions taking reference to std::error_code to set an error (and having an overload with the same name that throws an exception on error if you forget to pass the std::error_code)...I understand there's 30 years of history, but it still is annoying, that even the standard library is not consistent (or striving for consistency).

Then you top it on with `?` shortcut and the functional interface of Result and suddenly error handling becomes fun and easy to deal with, rather than just "return false" with a "TODO: figure out error handling".

replies(24): >>43908133 #>>43908158 #>>43908212 #>>43908219 #>>43908294 #>>43908381 #>>43908419 #>>43908540 #>>43908623 #>>43908682 #>>43908981 #>>43909007 #>>43909117 #>>43909521 #>>43910388 #>>43912855 #>>43912904 #>>43913484 #>>43913794 #>>43914062 #>>43914514 #>>43917029 #>>43922951 #>>43924618 #
1. jasonjmcghee ◴[] No.43908133[source]
unfortunately it's not so simple. that's the convention. depending on the library you're using it might be a special type of Error, or special type of Result, something needs to be transformed, `?` might not work in that case (unless you transform/map it), etc.

I like rust, but its not as clean in practice, as you describe

replies(5): >>43908198 #>>43908870 #>>43908919 #>>43908995 #>>43912581 #
2. koakuma-chan ◴[] No.43908198[source]
You can use anyhow::Result, and the ? will work for any Error.
3. ◴[] No.43908870[source]
4. ◴[] No.43908919[source]
5. ryandv ◴[] No.43908995[source]
There are patterns to address it such as creating your own Result type alias with the error type parameter (E) fixed to an error type you own:

    type Result<T> = result::Result<T, MyError>;

    #[derive(Debug)]
    enum MyError {
        IOError(String)
        // ...
    }
Your owned (i.e. not third-party) Error type is a sum type of error types that might be thrown by other libraries, with a newtype wrapper (`IOError`) on top.

Then implement the `From` trait to map errors from third-party libraries to your own custom Error space:

    impl From<io::Error> for MyError {
        fn from(e: io::Error) -> MyError {
            MyError::IOError(e.to_string())
        }
    }
Now you can convert any result into a single type that you control by transforming the errors:

    return sender
        .write_all(msg.as_bytes())
        .map_err(|e| e.into());
There is a little boilerplate and mapping between error spaces that is required but I don't find it that onerous.
replies(1): >>43910763 #
6. johnisgood ◴[] No.43910763[source]
I scratch my head when people try to justify Rust's implementation of X. It looks absolutely horrendous, IMO.

I would rather have what OCaml has: https://ocaml.org/docs/error-handling.

7. Cloudef ◴[] No.43912581[source]
You can use anyhow, but yeah zig generally does errors better IMO
replies(1): >>43915058 #
8. ziml77 ◴[] No.43915058[source]
Errors are where I find zig severely lacking. They can't carry context. Like if you're parsing a JSON file and it fails, you can know that it failed but not where it failed within the file. Their solution in the standard library for cases like this was to handle printing to stderr internally, but that is incredibly hacky.
replies(1): >>43922932 #
9. Cloudef ◴[] No.43922932{3}[source]
The std has diagnostics struct you can give to the json parser. Zig is manually memory managed language so it doesnt have payloads in errors for a good reason.
replies(1): >>43926714 #
10. ziml77 ◴[] No.43926714{4}[source]
Manual memory management is not a reason Zig couldn't have supported sum types for error returns. You don't need an allocator involved at all to return a discriminated union like Rust uses.

I actually find Zig quite a pleasant language other than my gripe with not handling more complex errors as cleanly.

replies(1): >>43930235 #
11. Cloudef ◴[] No.43930235{5}[source]
You would have to give up on global error set with sum types, unless you want to see the global error set bloat with the largest sum type. Also if the error payload needs allocating what are you gonna do? Allocating is no no no here. I know rust will just panic in OOM but zig chooses not to do that. Even if you allocated, since zig has no borrow checker the error handling becomes nightmare as you now have to deal with freeing the potential allocations.

Using out parameters for context, or diagnostic pattern like std does is not bad at all imo.

The only nice thing error payloads give you is better error messages in case of uncaught errors.