Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> But I fail to see how having convenience equates to ignoring the error.

The convenience of writing `?` means nobody will bother wrapping errors anymore. Is what I understand of this extremely dubious argument.

Since you could just design your `?` to encourage wrapping instead.



> Since you could just design your `?` to encourage wrapping instead.

Which is exactly what Rust does -- if the error returned by the function does not match the error type of `?` expression, but the error can be converted using the `From` trait, then the conversion is automatically performed. You can write out the conversion implementation manually, or derive it with a crate like thiserror:

    #[derive(Error)]
    enum MyError {
        #[error("Failed to read file")
        IoError(#[from] std::io::Error)
        // ...
    }

    fn foo() -> Result<(), MyError> {
        let data = std::fs::read("/some/file")?;
        // ...
    }
You can also use helper methods on Result (like `map_err`) for inserting explicit conversions between error types:

    fn foo() -> Result<(), MyError> {
        let data = std::fs::read("/some/file").map_err(MyError::IoError)?;
        // ...
    }


1. That is a global static relationship rather than a local one dynamic one, which is the sense in which Go users use wrapping.

2. Idiomatic go type erases errors, so you're converting from `error` to `error`, hence type-directed conversions are not even remotely an option.


`map_err` does not need to be type-directed; you can use an arbitrary function or closure. An enum variant can be used as a function mapping from the variant type to the error type, but we can do any arbitrary transformation:

    .map_err(|e| format!("Failed to read file: {e}")?;
But the "idiomatic Go" way of doing things sounds a lot closer to anyhow in Rust, which provides convenience utilities for dealing with type-erased errors:

    use anyhow::{Result, Context};

    fn foo() -> Result<()> {
        let data = std::fs::read("/some/file").context("Failed to read file")?;
        // ...
    }


Yes, I know that, but the argument (which, again, I called dubious) is that in both cases it's much easier to do just e.g.

  fn foo() -> Result<()> {
      let data = std::fs::read("/some/file")?;
      // ...
  }
whereas the current morass of Go's error handling means adding wrapping is not much more of a hassle.

But of course even if you accept that assertion you can just design your version of `?` such that wrapping is easier / not wrapping is harder (as it's still something you want) e.g. make it `?"value"` and `?nil` instead of `?`, or something.


> That is a global static relationship rather than a local one dynamic one, which is the sense in which Go users use wrapping.

In practice, the error type will be defined quite close to where the conversion is applied, so the static nature of it doesn’t feel too big.


You need to implement from for every type of error then? That seems pretty tedious also.


> The convenience of writing `?` means nobody will bother wrapping errors anymore.

A thread from two days ago bemoans this point:

https://news.ycombinator.com/item?id=44149809




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: