I am trying to find repetitions in an iterable sequence. Furthermore, I want to know the elements that occurred in that sequence up to that point.
I created a HashMap
and am trying to call insert
on it from within a closure used by take_while
. However, I have so far not managed to get it to compile due to type mismatches related to concrete / bound lifetimes.
Here is a simplified version of my code which exhibits the same error:
use std::collections::HashSet;
fn main() {
let mut seq = HashSet::new();
let mut insert = |k| seq.insert(k);
(1..10).cycle().take_while(insert);
}
Here are the errors I get:
error[E0631]: type mismatch in closure arguments
--> src/main.rs:6:21
|
5 | let mut insert = |k| seq.insert(k);
| ----------------- found signature of `fn(_) -> _`
6 | (1..10).cycle().take_while(insert);
| ^^^^^^^^^^ expected signature of `for<'r> fn(&'r {integer}) -> _`
error[E0271]: type mismatch resolving `for<'r> <[closure@src/main.rs:5:22: 5:39 seq:_] as std::ops::FnOnce<(&'r {integer},)>>::Output == bool`
--> src/main.rs:6:21
|
6 | (1..10).cycle().take_while(insert);
| ^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
How do I need to change the code for it to work?
This is in fact a borrowing error in disguise.
Iterator<Item = T>::take_while()
accepts a closure of kind FnMut(&T) -> bool
- that is, it passes each element to the closure by reference. This is pretty much natural because take_while()
must be able to yield the successfully tested element, so it can't pass it by value.
This means that insert
argument type is inferred to be &_
, and so HashSet
's generic parameter is also inferred as &_
. However, this means that you're attempting to store references to temporary values yielded by the cycle()
iterator to a structure which lives much longer. This is not allowed by borrowing rules. Unfortunately, Rust doesn't show exactly this reasoning because for some reason it can't infer that the numeric type is i32
and it also can't infer the correct lifetime parameter for the closure. That's what your error is about.
Instead, your closure should dereference the argument before storing it to the set. This works:
use std::collections::HashSet;
fn main() {
let mut seq = HashSet::new();
let mut insert = |&k: &i32| seq.insert(k);
(1..10).cycle().take_while(insert);
}
I had to add the full type of the argument too; as I said above, I think that the type inference is not powerful enough to deduce it.
BTW, you can actually get the borrow checker error if you specify the type explicitly:
use std::collections::HashSet;
fn main() {
let mut seq = HashSet::new();
let mut insert = |k: &i32| seq.insert(k); // no dereference
(1..10).cycle().take_while(insert);
}
The code above is equivalent to your original example except for the explicit type annotation, and it results in the following error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:5:43
|
5 | let mut insert = |k: &i32| seq.insert(k);
| ^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 5:22...
--> src/main.rs:5:22
|
5 | let mut insert = |k: &i32| seq.insert(k);
| ^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that expression is assignable (expected &i32, found &i32)
--> src/main.rs:5:43
|
5 | let mut insert = |k: &i32| seq.insert(k);
| ^
note: but, the lifetime must be valid for the block suffix following statement 1 at 5:5...
--> src/main.rs:5:5
|
5 | / let mut insert = |k: &i32| seq.insert(k);
6 | | (1..10).cycle().take_while(insert);
7 | | }
| |_^
note: ...so that variable is valid at time of its declaration
--> src/main.rs:5:9
|
5 | let mut insert = |k: &i32| seq.insert(k);
| ^^^^^^^^^^