Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can Rust not infer the proper lifetime in simple closures, or infers they are conflicting?

Tags:

rust

lifetime

I have not found any rules in the Rust documentation that would explain how lifetime elision applies to closures. Let's take a simple example:

fn foo(s: &str) {
    let id = |x: &str| x;
    println!("{}", id(s));
}

fn main() {
    foo("string");
}

I thought that the closure in the foo function would work similar to the following code:

fn foo(s: &str) {
    struct Id;  // A helper structure for closure
    impl Id {
        fn id(self: Self, x: &str) -> &str { &x }
    }
    let id = Id; // Creating a closure
    println!("{}", id.id(s));
}

The latter works fine, but the former fails to compile and produces a long error message about conflicting lifetime requirements:

t3.rs:2:24: 2:25 error: cannot infer an appropriate lifetime due to conflicting requirements [E0495]
t3.rs:2     let id = |x: &str| x;
                               ^
t3.rs:2:24: 2:25 note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 2:23...
t3.rs:2     let id = |x: &str| x;
                               ^
t3.rs:2:24: 2:25 note: ...so that expression is assignable (expected `&str`, found `&str`)
t3.rs:2     let id = |x: &str| x;
                               ^
<std macros>:3:11: 3:36 note: but, the lifetime must be valid for the expression at 3:10...
<std macros>:3 print ! ( concat ! ( $ fmt , "\n" ) , $ ( $ arg ) * ) ) ;
                         ^~~~~~~~~~~~~~~~~~~~~~~~~
<std macros>:2:25: 2:56 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
t3.rs:3:5: 3:27 note: in this expansion of println! (defined in <std macros>)
<std macros>:3:11: 3:36 note: ...so type `(&&str,)` of expression is valid during the expression
<std macros>:3 print ! ( concat ! ( $ fmt , "\n" ) , $ ( $ arg ) * ) ) ;
                         ^~~~~~~~~~~~~~~~~~~~~~~~~
<std macros>:2:25: 2:56 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
t3.rs:3:5: 3:27 note: in this expansion of println! (defined in <std macros>)
error: aborting due to previous error

I wonder why Rust cannot infer the proper lifetime in simple closures like the one that I wrote above. Moreover, why does the compiler think that there are conflicting requirements for lifetime.

like image 907
svat Avatar asked Feb 29 '16 13:02

svat


1 Answers

When you specify the type of a parameter or the return type in a closure, and that type is a reference, the compiler sets wrong expectations on the implicit lifetime parameter, and there's no way to define the lifetime parameter explicitly on a closure. This is a known issue. The workaround is to omit the parameter's type or the return type and let the compiler infer everything.

fn foo(s: &str) {
    let id = |x| x;
    println!("{}", id(s));
}

fn main() {
    foo("string");
}

If you still need to give a type hint, you can do so with a let binding inside the closure:

fn foo(s: &str) {
    let id = |x| { let y: &str = x; y };
    println!("{}", id(s));
}

fn main() {
    foo("string");
}
like image 105
Francis Gagné Avatar answered Sep 29 '22 08:09

Francis Gagné