简体   繁体   中英

Why must my error be valid for the static lifetime when trying to switch on a concrete error type?

I'm trying to implement a simple pattern: if I have some error I can try to recover my application, otherwise I just pop this exception to the caller:

use std::error::Error;

fn main() {
    let _ = sample();
}

fn sample() -> std::result::Result<i32, std::io::Error> {
    let result: Result<i32, std::io::Error> = Ok(10); // performing some operation
    match result {
        Ok(x) => Ok(x + 1),
        Err(e) => match e.cause() {
            // if it has any error
            Some(cause) => {
                // and it has any cause
                let io_error = cause.downcast_ref::<std::io::Error>(); // and this cause is IO error
                match io_error {
                    Some(_) => Ok(547), // return default value
                    None => Err(e),     // otherwise return an error
                }
            }
            None => Err(e),
        },
    }
}

I want to return x+1 if the operation succeeds. If it doesn't, but it's caused by an io::Error then return 547 . If it's caused by something else, just return an error as-is.

The current compiler error is:

error[E0597]: `e` does not live long enough
  --> src\main.rs:11:25
   |
11 |         Err(e) => match e.cause() { // if it's caused
   |                         ^ borrowed value does not live long enough
...
21 |     }
   |     - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

I don't understand why it says that it must have the static lifetime...

It's unfortunate, indeed, that the compiler is not more explicit .

Let me unwind this:

  1. Error::downcast_ref is only implemented for Error + 'static (thus, when self lifetime itself is 'static ),
  2. Thus in cause.downcast_ref , cause must be 'static ,
  3. Error::cause ties down the lifetime of its result to that of self ,
  4. Thus in e.cause() , e must be 'static ,
  5. e is a temporary introduced in Err(e) .

Hopefully, this is clearer.


I have not used it yet, however one of the Rust core team members (withoutboats) has been working on a new failure crate which supposedly solves a number of issue with the Error usage.

Because that's a requirement of the function you are using :

impl Error + 'static {
    pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T>
}

You cannot downcast trait objects that are not 'static .

The example code can be reduced further to make this more clear. Commenting out the downcast_ref allows the code to be compiled:

fn example(result: Result<i32, std::io::Error>) -> Result<i32, std::io::Error> {
    let e = result.unwrap_err();
    let cause = e.cause().unwrap();
    let _io_error = cause.downcast_ref::<std::io::Error>();
    unimplemented!()
}

This may just be an artifact of your reduced example code, but I don't understand why you are taking a Result<_, std::io::Error> and checking to see if the cause of the io::Error was another io::Error . If your Result was an Err , then you know that was an io:Error as it's the only possible thing. io::Error doesn't even provide a cause , unless you are using a custom error variant.

See also:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM