Hacker News new | ask | show | jobs
by svnpenn 1542 days ago
> Rust you can use the ? operator

That doesnt work with all types:

https://stackoverflow.com/a/65085003

2 comments

It works for any type that implements the `std::error::Error` trait; which is something you can easily implement for your own types. If you want your errors to be integers for some reason, you can wrap that type in a zero-sized "newtype" wrapper, and implement `Error` for that.

The Stack Overflow answer you linked seems to be claiming that it's simply easier to return strings, but I wouldn't say this is a restriction imposed by the language.

> easily implement for your own types

have you ever actually done that? I have, its not easy. Please dont try to hand wave away negatives of the Rust type system.

> have you ever actually done that? I have, its not easy.

Yes. I do it frequently. "#[derive(Error, Debug)]": https://github.com/dtolnay/thiserror#example

Much easier than implementing the error interface in go.

Rust is powerful enough to allow macros to remove annoying boiler-plate, and so most people using rust will grab one of the error-handling crates that are de-facto standard and remove the minor pain you're talking about.

In go, it's not really possible to do this because the language doesn't provide such macros (i.e. the old third-party github.com/pkg/errors wanted you to implement 'Cause', but couldn't provide sugar like 'this-error' does for it because go is simply less powerful).

I've found implementing errors in go to be much more error-prone and painful than in rust, and that's not to mention every function returning untyped errors, meaning I have no clue what callers should check for and handle new errors I add.

> Much easier than implementing the error interface in go.

is this a joke? You have to import a third party package, just to implement an error interface? Here is Go example, no imports:

    type errorString string

    func (e errorString) Error() string {
       return string(e)
    }
It was not a joke.

Let's look at a common example: you want to return two different types of errors and have the caller distinguish between them. Let me show it to you in rust and go.

Rust:

    #[derive(Error, Debug)]
    pub enum MyErrors {
       #[error("NotFound: {0}")
       NotFound(String),
       #[error("Internal error")]
       Internal(#[source] anyhow::Error),
    }
The equivalent go would be something like:

    type NotFoundErr struct {
        msg string
    }

    func (err NotFoundErr) Error() string {
        return "NotFound: " + err.msg
    }

    func (err NotFoundErr) Is(target error) bool {
        if target == nil {
            return false
        }
        // All NotFoundErrs are considered the same, regardless of msg
        _, ok := target.(NotFoundErr)
        return ok
    }

    type InternalErr struct {
        wrapped error
    }
    
    func (err InternalErr) Error() string {
        return fmt.Sprintf("Internal error: %s", err.wrapped)
    }

    func (err InternalErr) Unwrap() error {
        return err.wrapped
    }
I dont think you realize how ridiculous this comment is. Youre comparing 10 lines of Go, with 200 of Rust:

https://github.com/dtolnay/thiserror/blob/master/src/lib.rs

I do it frequently. It is indeed easy
Only automatically printing something when returning it from main doesn't work with all types with the ? operator. And frankly 'handling errors by auto print and exit' is a bit of a code smell anyway, it's not much better than just .unwrap() on everything in main.