Rust: Can鈥檛 declare lifetime for closure that returns a reference

Created on 14 Feb 2015  路  7Comments  路  Source: rust-lang/rust

When you declare closure argument types, there is no syntax to declare a lifetime parameter. And I guess lifetime elision does not apply to closures. Therefore, there seems to be no way to declare the type of a closure that returns a reference.

It compiles if you avoid declaring the type of the closure and depend on type inference. But then you would not be able to assign the closure to a local variable.

fn print_first(list: Vec<String>) {
    let x: &str = list
    .first()
    .map(|s: &String| -> &str &s[])  // ERROR 
    //.map(|s: &String| &s[]) // ERROR
    //.map(|s| -> &str &s[]) // ERROR
    //.map(|s| &s[])  // OK
    .unwrap_or("");
    println!("First element is {}", x);
}

It gives a compiler error and a suggestion that does not make sense.

    src/rusttest.rs:4:29: 4:32 error: cannot infer an appropriate lifetime for lifetime parameter 'a in function call due to conflicting requirements
    src/rusttest.rs:4   .map(|s: &String| -> &str &s[])
                                                   ^~~
    src/rusttest.rs:1:1: 10:2 help: consider using an explicit lifetime parameter as shown: fn print_first<'a>(list: Vec<String>)
    src/rusttest.rs:1 fn print_first(list: Vec<String>) {
    src/rusttest.rs:2   let x: &str = list
    src/rusttest.rs:3   .first()
    src/rusttest.rs:4   .map(|s: &String| -> &str &s[])  // ERROR
    src/rusttest.rs:5   //.map(|s: &String| &s[]) // ERROR
    src/rusttest.rs:6   //.map(|s| -> &str &s[]) // ERROR

This bug is filed after I asked this question on stack overflow. It may be related to Region inference fails for closure parameter #17004.

A-closures C-feature-request T-compiler

Most helpful comment

There is another problem related to this one:

error[E0281]: type mismatch: `[closure@src/x.rs:329:50: 335:22 message_type:_]` implements the trait `std::ops::Fn<(_,)>`, but the trait `for<'r> std::ops::Fn<(&'r Message,)>` is required
   --> src/x.rs:341:44
    |
329 |                       let filter_by_message_type = |x| {
    |  __________________________________________________-
330 | |                         if (x as &Message).get_type() == *message_type {
331 | |                             Some(x)
332 | |                         } else {
333 | |                             None
334 | |                         }
335 | |                     };
    | |_____________________- implements `std::ops::Fn<(_,)>`

which essentiall prevents using closures for for<'r> Fn(..) -> ...bounded generics

All 7 comments

Triage: there is still no way to declare lifetime parameters for closures like this today.

Lets throw some ideas out there:

|s: &'a String|<'a> -> &'a str &s[]
<'a>|s: &'a String| -> &'a str &s[]
let closure<'a> = |s: &'a String| -> &'a str &s[];

The last one is a little limited, but may actually be feasible unlike the others.

I just ran into this issue today, trying to return a reference out of a closure that had the same lifetime as one of its arguments. Closures with lifetime, as well and type arguments would definitely be nice to have - if we had them, then I'm pretty sure closures would be just as powerful as functions. There's an RFC to implement them both: https://github.com/rust-lang/rfcs/pull/1650

I played with a similar example today and found a funny workaround.
Just skip type declaration and cast the type in the body of the closure:

|s| -> &str { &(s as &String)[..] }

or even with #![feature(type_ascription)]

|s| -> &str { &(s: &String)[..] }

In this way, type inference can do it's job with a lifetime and the type of the argument is limited by the way of use in the body.

There is another problem related to this one:

error[E0281]: type mismatch: `[closure@src/x.rs:329:50: 335:22 message_type:_]` implements the trait `std::ops::Fn<(_,)>`, but the trait `for<'r> std::ops::Fn<(&'r Message,)>` is required
   --> src/x.rs:341:44
    |
329 |                       let filter_by_message_type = |x| {
    |  __________________________________________________-
330 | |                         if (x as &Message).get_type() == *message_type {
331 | |                             Some(x)
332 | |                         } else {
333 | |                             None
334 | |                         }
335 | |                     };
    | |_____________________- implements `std::ops::Fn<(_,)>`

which essentiall prevents using closures for for<'r> Fn(..) -> ...bounded generics

An update: all of the examples given in the bug description do compile today, after being updated for Rust 1.0 syntax:

fn print_first(list: Vec<String>) {
    let x: &str = list
    .first()
    // .map(|s: &String| -> &str { &s[..] })  // OK
    // .map(|s: &String| { &s[..] }) // OK
    // .map(|s| -> &str { &s[..] }) // OK
    .map(|s| { &s[..] })  // OK
    .unwrap_or("");
    println!("First element is {}", x);
}

fn main() {
    print_first(vec![format!("hello"), format!("world")]);
}

(However, what I do not yet know is whether the types we are actually inferring in all of the above cases are what the user expects. See related discussion on #56537...)

Was this page helpful?
0 / 5 - 0 ratings