简体   繁体   中英

Why does Rust not infer the right type for a closure passed to filter?

If I have the following code:

let a = 1;
let f = |n| n == &a;
let _: Vec<_> = (1u64..10).filter(f).collect();

Rust complains greatly that collect exists for the relevant Filter struct, but the trait bound FnMut is not satisfied by the closure.

However, if I inline the closure or annotate its argument type, the code works, such as:

let a = 1;
let _: Vec<_> = (1u64..10).filter(|n| n == &a).collect();

or:

let a = 1;
let f = |n: &u64| n == &a;
let _: Vec<_> = (1u64..10).filter(f).collect();

Why is this? The fact that inlining the closure without annotating the type works is truly bizarre. I would think it was because n was having its type inferred as u64 instead of &u64 because ranges have some kind of propensity towards getting consumed but I don't know.

I don't know the exact rule, but from what I've observed working with Rust, creating a closure without requiring a specific type always causes the type to be inferred from the information available in the declaration of the closure only — not how the closure value is used later. That is, Rust's type inference stops being “bidirectional” in this case. For example:

let f = |x| x.clone();
f("hello world");
error[E0282]: type annotations needed
  --> src/main.rs:11:14
   |
11 |     let f = |x| x.clone();
   |              ^ consider giving this closure parameter a type
   |
   = note: type must be known at this point

This example fails to compile because the compiler does not use the information from the call to f to decide that x should be &str .

In your case, I'm not sure exactly what the problem is.

  • I'd assume it was a lifetime problem (the parameter lifetime being inferred as the lifetime of the borrowed a rather than an arbitrary lifetime) but then I would think that |n: &u64| wouldn't help.

  • Another hypothesis is that the problem is that the == operator is a PartialEq::eq call in disguise, and it isn't inferring what the Self type of that call is (since some type other than u64 could implement PartialEq<&u64> . But then I'd expect to see another “type annotations needed” error, requiring you to narrow down which trait implementation is to be used.

I don't have a precise explanation, but generally you should expect that when you separate a closure's definition from its usage, you are likely to need to add more type annotations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM