Description
This example compiles as expected:
fn main() {
let mut list = vec![];
let mut add_to_list = |name: _| {
list.push(name);
};
let name = String::from("name1");
add_to_list(&name);
}
but when a type annotation is added to the closure argument, it fails:
- let mut add_to_list = |name: _| {
+ let mut add_to_list = |name: &str| {
Workaround
Sometimes type annotation is necessary when type inference fails. :
fn main() {
let mut list = vec![];
let mut add_to_list = |name: _| { //ERROR type annotations needed
if !name.is_empty() {
list.push(name);
}
};
let name = String::from("name1");
add_to_list(&name);
}
In this case you can use this ugly hack to annotate the type in the closure body:
let mut add_to_list = |name: _| {
+ let name: &str = name; // hack: annotate the type here to avoid rustc bug #xxx
if !name.is_empty() {
But why?
When rustc encounters such closure, It has to pick up one of these two types for the closure:
/// A closure that expects an argument of SOME specific lifetime, `'a`.
type FnSig1<'a> = dyn FnMut(&'a str);
/// A closure that expects an argument of ANY lifetime.
/// Aka higher-ranked lifetime.
type FnSig2 = dyn for<'a> FnMut(&'a str);
We want the first one here but the compiler is not smart enough to infer this. Instead it follows a set of dumb rules1 that leads it to the second type, and then it fails when borrow-checking the closure for the same reason the following fails:
fn test<'a, 'b>(mut list: Vec<&'a str>, name: &'b str) {
list.push(name);
}
There is a promising work on a-mir-fomality to make this inference smarter.
Footnotes
-
simply if the lifetime appears in type annotation, it's inferred to be higher-ranked ↩