Mysterious error messages around closures returning closures


#1

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.)

playground

/// 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]);
    
}

#2

I’m just as confused as you right now, but here’s one thing: annotating the types of the first function like the second function’s types are annotated makes it work fine.

    assert_eq!(first_run(0..10, |&n: &i32| if n > 3 { Some(|&m: &i32| m < 7) } else { None })
               .collect::<Vec<_>>(),
               vec![4,5,6]);

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bd34247556945be63b6f3048a031a7ab

Both succeed if m is type annotated, and both fail compiling if m isn’t.

Maybe type interference is going wrong and the error reporting for the first misses this fact for some reason?

"expected for<'r> fn(&'r i32) -> _, found fn(&_) -> _" could be saying “we want a function we know to be taking in &i32, but we have one which we don’t know what it takes in”.