0

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...

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151

2 Answers2

3

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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
2

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:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I don't understand how it affect this code. `'static` constraint is applied to `T` type which is return type and I really don't care about how long output reference is living. Or it may implicitly applied to `self`... – Alex Zhukovskiy Jan 02 '18 at 10:24
  • @AlexZhukovskiy I copied more of of the context of the function — it's implemented on `Error + 'static`. – Shepmaster Jan 02 '18 at 13:45
  • @Shepmaster: I'm feeling this diagnostic is missing a note (or a series, thereof) indicating *why* the compiler thinks that `e` should have a `'static` lifetime. It's really not immediately obvious here. – Matthieu M. Jan 02 '18 at 13:48
  • @MatthieuM. certainly, which is why your breakdown is good. You want to take my reduced example (maybe reduced further) and report an issue with it? – Shepmaster Jan 02 '18 at 13:51
  • 1
    @Shepmaster: There seems to be multiple issues about this error message already (such as [#43531](https://github.com/rust-lang/rust/issues/43531)) so I am not sure that it would be very helpful to add more. – Matthieu M. Jan 02 '18 at 14:26