confusing lifetime inference

1087a8a
Opened by Jay at 2024-12-21 05:10:31

Consider following snippet:

use std::error::Error;

use std::io::{self, ErrorKind};

fn caused_of<'a>(mut err: &'a (Error + 'static)) -> Option<&'a io::Error> {
    loop {
        match err.downcast_ref::<io::Error>() {
            None => match err.cause() {
                None => return None,
                Some(cause) => err = cause,
            },
            d => return d,
        }
    }
}

fn main() {
    let e = io::Error::new(ErrorKind::Other, "oh no!");
    println!("{:?}", caused_of(&e));
}

According to Book, cause's lifetime should be err's lifetime. which is 'a, so it should be safe to assign cause to err directly. But I got following warnnings:

test.rs:8:34: 8:39 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements [E0495]
test.rs:8                       None => match err.cause() {
                                           ^~~~~
test.rs:8:30: 8:41 note: first, the lifetime cannot outlive the method call at 8:29...
test.rs:8                       None => match err.cause() {
                                       ^~~~~~~~~~~
test.rs:8:30: 8:33 note: ...so that method receiver is valid for the method call
test.rs:8                       None => match err.cause() {
                                       ^~~
test.rs:5:75: 15:25 note: but, the lifetime must be valid for the lifetime 'a as defined on the block at 5:74...
test.rs:5 fn caused_of<'a>(mut err: &'a (Error + 'static)) -> Option<&'a io::Error> {
                                                                                    ^
test.rs:10:48: 10:53 note: ...so that trait type parameters matches those specified on the impl (expected std::ops::CoerceUnsized<&'a std::error::Error + 'static>, found std::ops::CoerceUnsized<&std::error::Error + 'static>)
test.rs:10                                                      Some(cause) => err = cause,
                                                          ^~~~~
error: aborting due to previous error

Am I missing something here?

  1. The problem is that fn cause returns a &'a (Error+'a) rather than an &'a (Error+'static).

    @nikomatsakis: that sounds like a good test case for the new regionck error messages.

    Ariel Ben-Yehuda at 2016-05-10 05:27:09

  2. Is there any good way to iterate all the cause of error to find if the specific error exists?

    Jay at 2016-05-10 05:37:07

  3. Current output:

    error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
     --> src/main.rs:8:31
      |
    8 |             None => match err.cause() {
      |                               ^^^^^
      |
    note: first, the lifetime cannot outlive the lifetime 'a as defined on the function body at 5:1...
     --> src/main.rs:5:1
      |
    5 | fn caused_of<'a>(mut err: &'a (Error + 'static)) -> Option<&'a io::Error> {
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    note: ...so that reference does not outlive borrowed content
     --> src/main.rs:8:27
      |
    8 |             None => match err.cause() {
      |                           ^^^
      = note: but, the lifetime must be valid for the static lifetime...
      = note: ...so that the expression is assignable:
              expected &'a std::error::Error + 'static
                 found &std::error::Error
    

    Update, minimal changes:

    error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
     --> src/main.rs:8:31
      |
    8 |             None => match err.cause() {
      |                               ^^^^^
      |
    note: first, the lifetime cannot outlive the lifetime 'a as defined on the function body at 5:14...
     --> src/main.rs:5:14
      |
    5 | fn caused_of<'a>(mut err: &'a (Error + 'static)) -> Option<&'a io::Error> {
      |              ^^
    note: ...so that reference does not outlive borrowed content
     --> src/main.rs:8:27
      |
    8 |             None => match err.cause() {
      |                           ^^^
      = note: but, the lifetime must be valid for the static lifetime...
      = note: ...so that the expression is assignable:
              expected &'a (dyn std::error::Error + 'static)
                 found &dyn std::error::Error
    

    Esteban Kuber at 2018-02-10 01:37:04

  4. Output with nll enabled:

    error: lifetime may not live long enough
      --> src/main.rs:11:32
       |
    6  | fn caused_of<'a>(mut err: &'a (Error + 'static)) -> Option<&'a io::Error> {
       |              -- lifetime `'a` defined here
    ...
    11 |                 Some(cause) => err = cause,
       |                                ^^^^^^^^^^^ cast requires that `'a` must outlive `'static`
    

    Esteban Kuber at 2019-07-20 02:31:59

  5. Triage: no change since nll.

    Esteban Kuber at 2023-10-19 18:36:42