使用 Peekable 实现 "cautious" take_while
Implementing a "cautious" take_while using Peekable
我想使用 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 error: cannot move out of borrowed content
:10 chars.by_ref().cautious_take_while(|&x| x != '.');
据我所知,我在函数签名方面遵循与 Rust 自己的 TakeWhile
相同的模式,但我看到了与借用检查器不同的不同行为。有人可以指出我做错了什么吗?
这是一个棘手的问题!我将先介绍代码的内容,然后尝试对其进行解释(如果我理解的话……)。它也是丑陋的无糖版本,因为我想减少附带的复杂性。
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 }
}
}
实际上,,所以我会遵从这一点,除非你想让我解释一些具体的事情。
by_ref()
的有趣之处在于它 returns 对自身的可变引用:
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
函数之所以有效,是因为它使用了特征 Iterator
,该特征会自动解析为 &mut Peekable<T>
。
但是你的代码不起作用,因为 Peekable
是一个结构,而不是特征,所以你的 CautiousTakeWhileable
必须指定类型,并且你试图取得它的所有权,但你不能,因为你有一个可变指针。
解决办法,不取Peekable<T>
而是取&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,}
}
}
这个解决方案的一个奇怪的副作用是现在不需要 by_ref
,因为 cautious_take_while()
采用可变引用,所以它不会窃取所有权。 take_while()
需要 by_ref()
调用,因为它可以采用 Peekable<T>
或 &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;
}
剩下的就比较简单了。
我想使用 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 error: cannot move out of borrowed content :10 chars.by_ref().cautious_take_while(|&x| x != '.');
据我所知,我在函数签名方面遵循与 Rust 自己的 TakeWhile
相同的模式,但我看到了与借用检查器不同的不同行为。有人可以指出我做错了什么吗?
这是一个棘手的问题!我将先介绍代码的内容,然后尝试对其进行解释(如果我理解的话……)。它也是丑陋的无糖版本,因为我想减少附带的复杂性。
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 }
}
}
实际上,
by_ref()
的有趣之处在于它 returns 对自身的可变引用:
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
函数之所以有效,是因为它使用了特征 Iterator
,该特征会自动解析为 &mut Peekable<T>
。
但是你的代码不起作用,因为 Peekable
是一个结构,而不是特征,所以你的 CautiousTakeWhileable
必须指定类型,并且你试图取得它的所有权,但你不能,因为你有一个可变指针。
解决办法,不取Peekable<T>
而是取&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,}
}
}
这个解决方案的一个奇怪的副作用是现在不需要 by_ref
,因为 cautious_take_while()
采用可变引用,所以它不会窃取所有权。 take_while()
需要 by_ref()
调用,因为它可以采用 Peekable<T>
或 &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;
}
剩下的就比较简单了。