use std::iter::Peekable;
pub trait AdvanceWhile<I: Iterator> {
fn advance_while<P>(&mut self, predicate: P)
where
P: Fn(&I::Item) -> bool;
}
impl<I: Iterator> AdvanceWhile<I> for Peekable<I> {
fn advance_while<P>(&mut self, predicate: P)
where
P: Fn(&I::Item) -> bool,
{
while let Some(val) = self.peek() {
if predicate(val) {
self.next();
} else {
break;
}
}
}
}
Playground
Error:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/main.rs:16:17
|
14 | while let Some(val) = self.peek() {
| ---- first mutable borrow occurs here
15 | if predicate(val) {
16 | self.next();
| ^^^^ second mutable borrow occurs here
...
20 | }
| - first borrow ends here
As Lukas Kalbertodt already said, this is a limitation of the borrow checker. Here I would like to show a more readable version:
fn advance_while<P>(&mut self, predicate: P)
where P: Fn(&I::Item) -> bool
{
while let Some(true) = self.peek().map(&predicate) {
self.next();
}
}
Typical case of lexical borrowing: the compiler is not yet able to understand that this is code is safe. So for the time being (until the so called non-lexical borrowing is implemented), try to rewrite your code. Like this, for example:
fn advance_while<P>(&mut self, predicate: P)
where P: Fn(&I::Item) -> bool
{
loop {
if let Some(val) = self.peek() {
if !predicate(val) {
break;
}
} else {
break;
}
self.next();
}
}
You could rewrite the function into this:
impl<I: Iterator> AdvanceWhile<I> for Peekable<I> {
fn advance_while<P>(&mut self, predicate: P)
where P: Fn(&I::Item) -> bool
{
loop {
{
let peek = match self.peek() {
Some(p) => p,
None => break,
};
if !predicate(peek) {
break;
}
}
self.next();
}
}
}
The problem is that your original code could do this:
while let Some(val) = self.peek() {
if predicate(val) {
self.next();
println!("{:?}", val);
} else {
break;
}
}
Accessing val
after you called next()
would allow you to access memory that was no longer valid, causing memory unsafety.
As others have pointed out, your code doesn't actually do this, but current Rust uses lexical lifetimes, an overly-conservative approximation of how long a reference needs to be valid for. Future Rust should help constrain the logic without introducing unsafety.
If your value implements Copy
, you can make use of that:
fn advance_while<P>(&mut self, predicate: P)
where
P: Fn(&I::Item) -> bool,
I::Item: Copy,
{
while let Some(&val) = self.peek() {
if predicate(&val) {
self.next();
} else {
break;
}
}
}
This causes the value to be copied from the reference returned by peek
. Since nothing is borrowed, the fact that you are going to mutate the iterator does not cause a problem.
If your type implements Clone
, you could clone the value, again disassociating it:
fn advance_while<P>(&mut self, predicate: P)
where
P: Fn(&I::Item) -> bool,
I::Item: Clone,
{
while let Some(val) = self.peek().cloned() {
if predicate(&val) {
self.next();
} else {
break;
}
}
}
If your type is neither Copy
nor Clone
, you need to introduce a temporary variable for the boolean result. This clearly informs the compiler that the borrow returned by peek
isn't needed beyond the equality check statement:
fn advance_while<P>(&mut self, predicate: P)
where
P: Fn(&I::Item) -> bool,
{
while self.peek().map_or(false, &predicate) {
self.next();
}
}
See also:
- Rust borrow of a HashMap lasts beyond the scope it's in?
Okay so this is not the best way of doing it but it might come in handy for more complicated cases...
fn advance_while<P>(&mut self, predicate: P)
where P: Fn(&I::Item) -> bool
{
while {
if let Some(val) = self.peek() {
predicate(val)
} else {
false
}
}
{
self.next();
}
}
Playpen