Here's a twitchy bit of code, with some tests at the bottom showing various attempts to use the first_run
function and the errors they elicit. I thought the first and second two forms were essentially equivalent, but apparently the choice between |&n| ... n ...
and |n| ... *n ...
somehow affects the inference of higher-ranked lifetimes on the closure's type?? Can someone help me understand why the language behaves this way?
(To be clear: I don't need help making it run. I'd greatly appreciate help understanding why the changes to the code affect the compiler's behavior as they do.)
/// Return an iterator over the first consecutive run of `iter`'s items
/// indicated by `start`.
///
/// Drop the initial run of items from `iter` until `start(item)` returns
/// `Some(extend)`, for some closure `extend`. Then, produce `item` and all
/// further items from `iter` for as long as `extend` returns true. Once
/// `extend` returns `false`, iteration is over.
pub fn first_run<'a, I, S, E>(iter: I, start: S) -> FirstRun<I, S, E>
where I: Iterator,
S: 'a + FnMut(&I::Item) -> Option<E>,
E: 'a + FnMut(&I::Item) -> bool,
{
FirstRun::NotYet { iter, start }
}
pub enum FirstRun<I, S, E> {
NotYet { iter: I, start: S },
Extending { iter: I, extend: E },
Done,
}
impl<I, S, E> Iterator for FirstRun<I, S, E>
where I: Iterator,
S: FnMut(&I::Item) -> Option<E>,
E: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
match std::mem::replace(self, FirstRun::Done) {
FirstRun::NotYet { mut iter, mut start } => {
// Drop items until we get one that `start` likes.
while let Some(item) = iter.next() {
if let Some(extend) = start(&item) {
*self = FirstRun::Extending { iter, extend };
return Some(item);
}
}
*self = FirstRun::Done;
None
}
FirstRun::Extending { mut iter, mut extend } => {
if let Some(item) = iter.next() {
if extend(&item) {
*self = FirstRun::Extending { iter, extend };
Some(item)
} else {
*self = FirstRun::Done;
None
}
} else {
*self = FirstRun::Done;
None
}
}
FirstRun::Done => None,
}
}
}
#[test]
fn test_first_run() {
/*
error[E0631]: type mismatch in closure arguments
--> src/lib.rs:62:16
|
62 | assert_eq!(first_run(0..10, |&n| if n > 3 { Some(|&m| m < 7) } else { None })
| ^^^^^^^^^ ---------- found signature of `fn(&_) -> _`
| |
| expected signature of `for<'r> fn(&'r {integer}) -> _`
|
note: required by `first_run`
--> src/lib.rs:8:1
assert_eq!(first_run(0..10, |&n| if n > 3 { Some(|&m| m < 7) } else { None })
.collect::<Vec<_>>(),
vec![4,5,6]);
*/
/*
At least this error message has a helpful suggestion:
error[E0282]: type annotations needed
--> src/lib.rs:77:55
|
77 | assert_eq!(first_run(0..10, |n| if *n > 3 { Some(|m| *m < 7) } else { None })
| ^ consider giving this closure parameter a type
|
= note: type must be known at this point
assert_eq!(first_run(0..10, |n| if *n > 3 { Some(|m| *m < 7) } else { None })
.collect::<Vec<_>>(),
vec![4,5,6]);
*/
// works:
assert_eq!(first_run(0..10, |n: &i32| if *n > 3 { Some(|m: &i32| *m < 7) } else { None })
.collect::<Vec<_>>(),
vec![4,5,6]);
}