【问题标题】:Implementing a "cautious" take_while using Peekable使用 Peekable 实现“谨慎”的 take_while
【发布时间】:2015-04-30 20:20:21
【问题描述】:

我想使用Peekable 作为新cautious_take_while 操作的基础,该操作类似于IteratorExt 中的take_while,但不消耗第一个失败的项目。 (还有一个问题是这是否是一个好主意,以及是否有更好的方法来在 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")

我在creating a MCVE here 上试了一下,但我得到了:

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

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

【问题讨论】:

    标签: iterator rust traits borrow-checker


    【解决方案1】:

    by_ref() 的有趣之处在于它返回一个对自身的可变引用:

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

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

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

    标准的take_while 函数之所以有效,是因为它使用特征Iterator,它会自动解析为&amp;mut Peekable&lt;T&gt;

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

    解决方案,不要使用Peekable&lt;T&gt;,而是使用&amp;mut Peekable&lt;T&gt;。您还需要指定生命周期:

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

    这个解决方案的一个奇怪的副作用是现在不需要by_ref,因为cautious_take_while() 采用可变引用,所以它不会窃取所有权。 take_while() 需要 by_ref() 调用,因为它可以采用 Peekable&lt;T&gt;&amp;mut Peekable&lt;T&gt;,并且默认为第一个。通过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;
    }
    

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

    【讨论】:

    • 谢谢@rodrigo!我已将您的第一个建议纳入is.gd/NalTYL 以生成一个工作示例。但是当我尝试将类型输入到结构中时,如is.gd/6c64vf,我得到error: the trait *core::clone::Clone* is not implemented for the type *&amp;mut core::iter::Peekable&lt;T&gt;*,我似乎无法通过将+ Clone 添加到第43 行的特征边界来克服。
    • @Bosh。不确定,但我认为不能克隆可变指针。您的变体接受Clone 可能是因为Peekable 明确地实现了Clone。也许你也可以这样做,但是代码需要一些重构......
    • 太棒了。我停止从Clone 派生并清理了时间线:is.gd/ljjJAE。再次感谢您的帮助和解释!
    • @Bosh:很高兴能提供帮助,我也学到了很多东西。看起来您的原始版本接受了#[derive(clone],因为对于参数类型,如果编译器无法证明它永远不会工作,则编译器会接受该指令。但是,它可能会在特定类型中默默地失败。例如#derive(Clone)] struct X&lt;T&gt;(T); 将编译,但实际的X::clone() 只有在T 可克隆时才可用。
    【解决方案2】:

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

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

    实际上,Rodrigo seems to have a good explanation,所以我会推迟,除非你想让我解释一些具体的事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多