Mutability borrow errors on peeking and incrementing an iterator


#1

I’m getting mutable borrow errors when I try to mutate a iterator reference conditionally based off the result of peek():

fn increment_iter<'a>(iter: &mut Peekable<Chars<'a>>) -> Option<()> {
    None
}

fn test_peek<'a>(mut iter: Peekable<Chars<'a>>) {
    match iter.peek() {
        Some(x) => if x == &'$' {
            increment_iter(&mut iter)
        } else {
            None
        },
        None => None,
    };
}

I understand that the mutable borrow is held for the entirety of the match statement, but is there any way to “restrict” the borrow so that I can increment the iterator based off the result of the peek?

So far I have the following workaround which works, but feels like an ugly hack:

fn test_peek<'a>(mut iter: Peekable<Chars<'a>>) {
    if let Some(true) = iter.peek().map(|x| if x == &'$' { true } else { false }) {
        increment_iter(&mut iter);
    }
}

Playground Link


#2

Well you could get rid of the if inside the map, but that’s about it

fn test_peek<'a>(mut iter: Peekable<Chars<'a>>) {
    if let Some(true) = iter.peek().map(|x| x == &'$') {
        increment_iter(&mut iter);
    }
}

Once the 2018 version arrives your code will compile fine, due to the addition of nll!


#3

You can also use map_or:

fn test_peek<'a>(mut iter: Peekable<Chars<'a>>) {
    if iter.peek().map_or(false, |x| x == &'$') {
        increment_iter(&mut iter);
    }
}

#4

There’s no need for a map in this example:

if let Some('$') = iter.peek() {
     increment_iter(&mut iter);
}

#5

Damn, I must have been really tired yesterday to have missed that.


#6

Yeah, I don’t think the map is needed actually in this example. I was trying to simplify it from something more complex, like calling another function to check on the x value in the if conditional that isn’t so easily expressed using the pattern match syntax, hence the .map.

Am I right to say that this falls under case #2 in the NLL doc?

fn test_peek<'a>(mut iter: Peekable<Chars<'a>>) {
    match iter.peek() {                   //  'a   'b
        Some(x) => if x == &'$' {         //   |    v
            increment_iter(&mut iter)     //   |
        } else {                          //   |
            None                          //   |
        },                                //   |
        None => None,                     //   |
    };                                    //   v
}

My understanding is that without NLL, the borrow checker detects the lifetime of the peek borrow as 'a as annotated above, but with NLL, it is able to more intelligently see that the value x is no longer used and can be released, as indicated by the lifetime 'b?