如何根据Peekable :: peek的结果调用Peekable :: next?

时间:2016-05-29 09:08:56

标签: rust

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[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

5 个答案:

答案 0 :(得分:3)

词汇借用的典型案例:编译器尚不能理解这是代码是安全的。因此,暂时(直到实施所谓的非词汇借用),尝试重写您的代码。像这样,例如:

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();
    }
}

答案 1 :(得分:3)

正如Lukas Kalbertodt已经说过,这是借阅检查员的限制。在这里,我想展示一个更易读的版本:

fn advance_while<P>(&mut self, predicate: P)
    where P: Fn(&I::Item) -> bool 
{
    while let Some(true) = self.peek().map(&predicate) {
        self.next();
    }
}

答案 2 :(得分:1)

您可以将此功能重写为:

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();
        }
    }
}

答案 3 :(得分:1)

问题是您的原始代码可以执行此操作:

while let Some(val) = self.peek() {
    if predicate(val) {
        self.next();
        println!("{:?}", val);
    } else {
        break;
    }
}

调用val后访问next()将允许您访问不再有效的内存,从而导致内存不安全。

正如其他人所指出的,你的代码并没有实际这样做,但是当前的Rust使用 lexical lifetimes ,这是一个过于保守的近似值参考需要有效。 Future Rust应该有助于限制逻辑,而不会引起不安全。

如果您的值实现Copy,您可以使用它:

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;
        }
    }
}

这会导致从peek返回的引用中复制值。由于没有任何借用,你要改变迭代器的事实不会引起问题。

如果您的类型实现Clone,您可以克隆该值,再次取消它的关联:

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;
        }
    }
}

如果您的类型既不是Copy也不是Clone,则需要为布尔结果引入临时变量。这清楚地告诉编译器,除了等式检查语句之外,peek返回的借用是不需要的:

fn advance_while<P>(&mut self, predicate: P)
where
    P: Fn(&I::Item) -> bool,
{
    while self.peek().map_or(false, &predicate) {
        self.next();
    }
}

另见:

答案 4 :(得分:0)

好的,所以这不是最好的方法,但对于更复杂的案例,它可能会派上用场......

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