How to handle different error types after boxing t

2019-08-20 10:28发布

问题:

This question already has an answer here:

  • How do you define custom `Error` types in Rust? 3 answers
  • How to get a reference to a concrete type from a trait object? 2 answers
  • Rust proper error handling (auto convert from one error type to another with question mark) 3 answers

In my try to better handle errors and prevent repeating tio much code, I am implementing the ?, operator returning Result<bool, Box<error::Error>> so that I could just match the error once instead of multiple times.

I changed the following code using multiple matches:

fn example(pool: mysql::Pool) {
    let now = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
        Ok(n) => n.as_secs(),
        Err(_) => panic!("SystemTime before UNIX EPOCH!"),
    };

    // create table
    match pool.prep_exec("CREATE TABLE IF NOT EXISTS dbpulse_rw (id INT NOT NULL, t INT(11) NOT NULL, PRIMARY KEY(id))", ()) {
        Ok(_) => (),
        Err(e) => {
            eprintln!("{}", e);
            return;
        }
    }

    // write into table
    let mut stmt = match pool
        .prepare("INSERT INTO dbpulse_rw (id, t) VALUES (1, ?) ON DUPLICATE KEY UPDATE t=?")
    {
        Ok(stmt) => stmt,
        Err(e) => {
            eprintln!("{}", e);
            return;
        }
    };

    match stmt.execute((now, now)) {
        Ok(_) => (),
        Err(mysql::Error::IoError(e)) => {
            eprintln!("IoError: {}", e);
            // send alert
            return;
        }
        Err(e) => {
            eprintln!("{}", e);
            return;
        }
    }

    let items = match pool.prep_exec("SELECT t FROM dbpulse_rw WHERE id=1", ()) {
        Ok(n) => n,
        Err(mysql::Error::IoError(e)) => {
            eprintln!("IoError: {}", e);
            //send_alert
            return;
        }
        Err(e) => {
            eprintln!("{}", e);
            return;
        }
    };
    for row in items {
        let pool = pool.clone();
        let rs = mysql::from_row::<u64>(row.unwrap());
        if now != rs {
            // send alert
        }
        assert_eq!(now, rs);
    }
}

To use the ? operator:

fn example(pool: mysql::Pool) -> Result<bool, Box<error::Error>> {
    let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
    let now = n.as_secs();

    // create table
    pool.prep_exec("CREATE TABLE IF NOT EXISTS dbpulse_rw (id INT NOT NULL, t INT(11) NOT NULL, PRIMARY KEY(id))", ())?;

    // write into table
    let mut stmt = pool
        .prepare("INSERT INTO dbpulse_rw (id, t) VALUES (1, ?) ON DUPLICATE KEY UPDATE t=?")?;
    stmt.execute((now, now))?;

    pool.prep_exec("SELECT t FROM dbpulse_rw WHERE id=1", ())
        .map(|items| {
            for row in items {
                match row {
                    Ok(row) => {
                        let rs = mysql::from_row::<u64>(row);
                        if now != rs {
                            // send alert
                        }
                    }
                    Err(e) => println!("{}", e),
                }
            }
        })?;

    Ok(true)
}

What I would like to do now is to match based on the error, either a timeout or a query error, or other error type. I am creating the MySQL pool like this:

let mut opts = mysql::OptsBuilder::from_opts(dsn);
opts.stmt_cache_size(0);
opts.read_timeout(Some(Duration::new(3, 0)));
opts.write_timeout(Some(Duration::new(3, 0)));
let pool = mysql::Pool::new_manual(1, 5, opts).expect("Could not connect to MySQL");

If a query takes more than 3 seconds it will return a mysql::Error::IoError the one I would like to distinguish between other possible errors, so far I have tried:

fun run_example() {
    match example() {
       Ok(_) => (),
       Err(mysql::Error::IoError(e)) => {
          eprintln!("IoError: {}", e);
          // send alert
          return;
       }
       Err(e) => {
         eprintln!("{}", e);
         return;
       }
    }
}

But I am getting a mismatched types error:

expected struct `std::boxed::Box`, found enum `mysql::error::Error`

Any tips or better ideas about how to implement this?

回答1:

You lose the concrete type information by using a trait. Some error crate could help you, but this implies casting a trait to a concrete type and so could fail at runtime.

I advise you to create a type MyError and list all potential errors that your function could produce. You can then match the real error nicely and fast. For example, use SNAFU