2
\$\begingroup\$

I am learning Rust and I would like to know what is the idiomatic way of writing a function that could run into multiple error types. For instance, in the function get_account_with_creds I am trying to return a struct AccountForResponse if the credentials match. The function could possibly run into a database error, so the Result could be a PoolError. I am using unwrap a few places because the error types are different. How can I improve this? I am also not sure about returning Ok(None)

pub async fn get_account_with_creds(db: &Db, creds: AccountForLogin) -> Result<Option<AccountForResponse>, PoolError> {
    let client: Client = db.get().await?;
    let stmt = client
        .prepare("SELECT id, first_name, last_name, email, password from accounts WHERE email = $1")
        .await?;
    let rows = client.query(&stmt, &[&creds.email]).await?;
    let row = rows.first().unwrap();
    let hashed_password: &str = row.get(4);
    match bcrypt::verify(creds.password, hashed_password).unwrap() {
        false => Ok(None),
        true => {
            let account = Some(AccountForResponse {
                id: Some(row.get(0)),
                first_name: row.get(1),
                last_name: row.get(2),
                email: row.get(3),
            });
            Ok(account)
        }
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ The anyhow crate might be what you're looking for. \$\endgroup\$ Commented Jun 22, 2021 at 18:54
  • \$\begingroup\$ Thanks, it seems like it is exactly what I am looking for. But is using anyhow the idiomatic way of handling returning multiple error types in Rust? I intend to use it everywhere I face this issue \$\endgroup\$ Commented Jun 22, 2021 at 19:01

1 Answer 1

5
\$\begingroup\$
  • If you expect to handle the errors (to recover from them in a way that is specific to which error occurred), or if you're writing a library, then define an enum type which can hold any of the errors might occur. Each enum variant might either contain the original error value (so it can be retrieved), or not (if the original error is purely an implementation detail). The thiserror crate can help you implement Error and From for such enums.

    #[derive(thiserror::Error)]
    enum LoginError {
        Database(#[from] PoolError),
        AccountNotFoundOrIncorrectPassword,  // Use this in place of your unwraps
    }
    
  • If you simply want to collect the errors and report/log them, without the code worrying about the differences between different errors (because you don't have a specific plan beyond "log it and/or abort"), then you can use anyhow, or stick to the standard library and use Box<dyn std::error::Error> — there are provided From/Into conversions for it so that the ? operator can turn any error that implements std::error::Error into the boxed form.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.