1. 15
  1.  

  2. 7

    you have to produce custom error types to use in a function or method that can error in more than one way.

    This is an area where Zig really shines. Automatic error unions, required error handling, errdefer, and the try keyword really give it my favorite error handling feel of any language.

    1. 14

      You don’t have to actually, just use anyhow or any of the other libs that do Box<dyn Error> automatically for you. It’s meant for applications and I’m happily using that.

      Edit: Also you can get things like backtraces for free on top with crates like stable-eyre

      1. 8

        Yeah this is what my team does. We use anyhow for applications, thiserror for libraries. It’s nicer than what I had in Haskell.

        Only other thing we had to make was an actix-compatible error helper.

        This:

        
        pub trait IntoHttpError<T> {
            fn http_error(
                self,
                message: &str,
                status_code: StatusCode,
            ) -> core::result::Result<T, actix_web::Error>;
        
            fn http_internal_error(self, message: &str) -> core::result::Result<T, actix_web::Error>
            where
                Self: std::marker::Sized,
            {
                self.http_error(message, StatusCode::INTERNAL_SERVER_ERROR)
            }
        }
        
        impl<T, E: std::fmt::Debug> IntoHttpError<T> for core::result::Result<T, E> {
            fn http_error(
                self,
                message: &str,
                status_code: StatusCode,
            ) -> core::result::Result<T, actix_web::Error> {
                match self {
                    Ok(val) => Ok(val),
                    Err(err) => {
                        error!("http_error: {:?}", err);
                        Err(error::InternalError::new(message.to_string(), status_code).into())
                    }
                }
            }
        }
        

        Lets us do this:

            let conn = app_state
                .db
                .get()
                .http_internal_error("Could not get database connection")?;
        ...
            let job_events = models::get_recent_job_events(&conn)
                .http_internal_error("Could not query datasets from the database")?;
        
        1. 1

          yeah it’s a bit sad they settled on failure in an incompatible way, but oh well - they can’t really change that until the next major version and back then it was a sensible approach - and to be fair you may just want to manually decide what happens when a specific error reaches the actix stack, to give some different response. I actually use that on purpose to return specific json / status codes when for example a user isn’t existing.

          1. 2

            I’m not sure what they should’ve done differently. I wouldn’t want anyhow errors silently turning into 500 errors with no explicit top-level message for API consumers and end-users to receive. I also wouldn’t want API concerns to infect the rest of the crate.