Can’t declare lifetime for closure that returns a reference
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.
Triage: there is still no way to declare lifetime parameters for closures like this today.
Steve Klabnik at 2016-03-04 20:11:40
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.
Diggory Hardy at 2016-05-17 17:34:32
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
Michael Hewson at 2017-02-12 21:28:09
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.
Alexander Irbis at 2017-04-04 22:50:13
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 genericsBernhard Schuster at 2017-10-27 07:24:13
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")]); }Felix S Klock II at 2018-12-06 09:34:10
(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...)
Felix S Klock II at 2018-12-06 09:35:26
I've been having a somewhat similar issue being unable to handle lifetimes with closures. Were explicit lifetimes ever added to closures?
$ rustup --version rustup 1.24.1 (a01bd6b0d 2021-04-27) info: This is the version for the rustup toolchain manager, not the rustc compiler. info: The currently active `rustc` version is `rustc 1.52.1 (9bc8c42bb 2021-05-09)` $ cargo --version cargo 1.52.0 (69767412a 2021-04-21) $ rustc --version rustc 1.52.1 (9bc8c42bb 2021-05-09)#[cfg(test)] mod tests { #[test] fn closure_lifetimes() { let input: String = String::from("hello world"); let closure = |s: &str| -> &str {&s[..]}; let output: &str = closure(&input); assert_eq!(input, output); } }error: lifetime may not live long enough --> src/payload.rs:173:42 | | let closure = |s: &str| -> &str {&s[..]}; | - - ^^^^^^ returning this value requires that `'1` must outlive `'2` | | | | | let's call the lifetime of this reference `'2` | let's call the lifetime of this reference `'1`Deleted user at 2021-05-13 23:57:25
Isn't the compiler capable of detecting that the argument outlives the closure? Example:
fn main() { let x = SimpleWrapper { value: 10 }; let foo = |wrapper: &SimpleWrapper| wrapper; let y = foo(&x); } struct SimpleWrapper { value: i32, }Nico Teufel at 2022-02-03 00:40:34
Sometimes, I use a “cast function” to declare lifetimes:
fn main() { fn cast<T>(x: T) -> T where T: for<'a> Fn(&'a SimpleWrapper) -> &'a SimpleWrapper { x } let x = SimpleWrapper { value: 10 }; let foo = cast(|wrapper: &SimpleWrapper| wrapper); let y = foo(&x); } struct SimpleWrapper { value: i32, }Jörg Sommer at 2022-05-29 05:49:00