5

我想Peekable用作新操作的基础,该cautious_take_while操作类似于 take_whilefromIteratorExt但不消耗第一个失败的项目。(还有一个问题是这是否是一个好主意,以及是否有更好的方法来在 Rust 中实现这个目标——我很乐意得到这个方向的提示,但主要是我试图理解我的代码在哪里破)。

我试图启用的 API 基本上是:

let mut chars = "abcdefg.".chars().peekable();

let abc : String = chars.by_ref().cautious_take_while(|&x| x != 'd');
let defg : String = chars.by_ref().cautious_take_while(|&x| x != '.');

// yielding (abc = "abc", defg = "defg")

我在这里创建了一个 MCVE,但我得到了:

:10:5: 10:19 错误:无法移出借用内容 :10 chars.by_ref().cautious_take_while(|&x| x != '.');

据我所知,TakeWhile就函数签名而言,我遵循与 Rust 自己相同的模式,但我看到了与借用检查器不同的不同行为。有人可以指出我做错了什么吗?

4

2 回答 2

5

有趣的by_ref()是它返回一个对自身的可变引用:

pub trait IteratorExt: Iterator + Sized {
    fn by_ref(&mut self) -> &mut Self { self }
}

它之所以有效,是因为该Iterator特征是为指向 Iterator类型的可变指针实现的。聪明的!

impl<'a, I> Iterator for &'a mut I where I: Iterator, I: ?Sized { ... }

标准函数之所以有效,是因为它使用了自动解析为take_while的 trait 。Iterator&mut Peekable<T>

但是你的代码不起作用,因为Peekable它是一个结构,而不是一个特征,所以你CautiousTakeWhileable必须指定类型,并且你试图获得它的所有权,但你不能,因为你有一个可变指针。

解决方案,不要采取Peekable<T>but &mut Peekable<T>。您还需要指定生命周期:

impl <'a, T: Iterator, P> Iterator for CautiousTakeWhile<&'a mut Peekable<T>, P>
where P: FnMut(&T::Item) -> bool {
     //...
}

impl <'a, T: Iterator> CautiousTakeWhileable for &'a mut Peekable<T> {
    fn cautious_take_while<P>(self, f: P) -> CautiousTakeWhile<&'a mut Peekable<T>, P>
     where P: FnMut(&T::Item) -> bool {
        CautiousTakeWhile{inner: self, condition: f,}
    }
}

这个解决方案的一个奇怪的副作用是 nowby_ref不需要,因为cautious_take_while()它需要一个可变引用,所以它不会窃取所有权。by_ref()调用是必需的,因为take_while()它可以采用Peekable<T>or &mut Peekable<T>,并且默认为第一个。通过by_ref()调用,它将解析为第二个。

现在我终于明白了,我认为更改 的定义struct CautiousTakeWhile以将可窥视位包含到结构本身中可能是一个好主意。如果我是对的,困难在于必须手动指定生命周期。就像是:

struct CautiousTakeWhile<'a, T: Iterator + 'a, P> 
    where T::Item : 'a {
    inner: &'a mut Peekable<T>,
    condition: P,
}
trait CautiousTakeWhileable<'a, T>: Iterator {
    fn cautious_take_while<P>(self, P) -> CautiousTakeWhile<'a, T, P> where
        P: FnMut(&Self::Item) -> bool;
}

其余的或多或少是直截了当的。

于 2015-02-28T01:53:53.270 回答
1

这是一个棘手的问题!我将首先介绍代码的内容,然后尝试解释它(如果我理解的话......)。它也是丑陋的、不加糖的版本,因为我想减少附带的复杂性。

use std::iter::Peekable;

fn main() {
    let mut chars = "abcdefg.".chars().peekable();

    let abc: String = CautiousTakeWhile{inner: chars.by_ref(), condition: |&x| x != 'd'}.collect();
    let defg: String = CautiousTakeWhile{inner: chars.by_ref(), condition: |&x| x != '.'}.collect();
    println!("{}, {}", abc, defg);
}

struct CautiousTakeWhile<'a, I, P> //'
    where I::Item: 'a, //'
          I: Iterator + 'a, //'
          P: FnMut(&I::Item) -> bool,
{
    inner: &'a mut Peekable<I>, //'
    condition: P,
}

impl<'a, I, P> Iterator for CautiousTakeWhile<'a, I, P>
    where I::Item: 'a, //'
          I: Iterator + 'a, //'
          P: FnMut(&I::Item) -> bool
{
    type Item = I::Item;

    fn next(&mut self) -> Option<I::Item> {
        let return_next =
            match self.inner.peek() {
                Some(ref v) => (self.condition)(v),
                _ => false,
            };
        if return_next { self.inner.next() } else { None }
    }
}

实际上,罗德里戈似乎有一个很好的解释,所以我会顺其自然,除非你想让我解释一些具体的事情。

于 2015-02-28T01:52:54.973 回答