我试图实现一个同时遍历两个迭代器的函数,为每对调用一个函数。此回调可以通过返回(bool, bool)
元组来控制每个步骤中哪些迭代器前进。由于迭代器在我的用例中引用缓冲区,因此它们无法实现stdlib中的Iterator
特征,而是通过next_ref
函数使用,该函数与{ {1}},但需要额外的生命周期参数。
Iterator::next
// An iterator-like type, that returns references to itself
// in next_ref
struct RefIter {
value: u64
}
impl RefIter {
fn next_ref<'a>(&'a mut self) -> Option<&'a u64> {
self.value += 1;
Some(&self.value)
}
}
// Iterate over two RefIter simultaneously and call a callback
// for each pair. The callback returns a tuple of bools
// that indicate which iterators should be advanced.
fn each_zipped<F>(mut iter1: RefIter, mut iter2: RefIter, callback: F)
where F: Fn(&Option<&u64>, &Option<&u64>) -> (bool, bool)
{
let mut current1 = iter1.next_ref();
let mut current2 = iter2.next_ref();
loop {
let advance_flags = callback(¤t1, ¤t2);
match advance_flags {
(true, true) => {
current1 = iter1.next_ref();
current2 = iter2.next_ref();
},
(true, false) => {
current1 = iter1.next_ref();
},
(false, true) => {
current2 = iter1.next_ref();
},
(false, false) => {
return
}
}
}
}
fn main() {
let mut iter1 = RefIter { value: 3 };
let mut iter2 = RefIter { value: 4 };
each_zipped(iter1, iter2, |val1, val2| {
let val1 = *val1.unwrap();
let val2 = *val2.unwrap();
println!("{}, {}", val1, val2);
(val1 < 10, val2 < 10)
});
}
我明白为什么会抱怨,但无法找到解决办法。我很欣赏这个主题的任何帮助。
中链接到此代码段答案 0 :(得分:5)
由于迭代器在我的用例中引用了一个缓冲区,它们不能从stdlib实现
Iterator
特征,而是通过next_ref
函数使用,这是与Iterator::next
相同,但需要额外的生命周期参数。
您正在描述流式迭代器。有一个箱子,恰当地称为streaming_iterator。文档描述了您的问题(强调我的):
虽然标准
Iterator
特征的功能基于next
方法,StreamingIterator
的功能基于。{ 一对方法:advance
和get
。这基本上分裂了next
的逻辑为一半(事实上,StreamingIterator
&#39;next
方法 除了致电advance
后跟get
)之外什么都不做。这是必要的,因为Rust的借词词汇处理(更多 特别是缺少单一进入,多次退出借入)。 如果
StreamingIterator
被定义为Iterator
只需要一个next
方法,filter
等操作无法定义。
crate目前没有zip
功能,当然也不是你所描述的变种。但是,它很容易实现:
extern crate streaming_iterator;
use streaming_iterator::StreamingIterator;
fn each_zipped<A, B, F>(mut iter1: A, mut iter2: B, callback: F)
where
A: StreamingIterator,
B: StreamingIterator,
F: for<'a> Fn(Option<&'a A::Item>, Option<&'a B::Item>) -> (bool, bool),
{
iter1.advance();
iter2.advance();
loop {
let advance_flags = callback(iter1.get(), iter2.get());
match advance_flags {
(true, true) => {
iter1.advance();
iter2.advance();
}
(true, false) => {
iter1.advance();
}
(false, true) => {
iter1.advance();
}
(false, false) => return,
}
}
}
struct RefIter {
value: u64
}
impl StreamingIterator for RefIter {
type Item = u64;
fn advance(&mut self) {
self.value += 1;
}
fn get(&self) -> Option<&Self::Item> {
Some(&self.value)
}
}
fn main() {
let iter1 = RefIter { value: 3 };
let iter2 = RefIter { value: 4 };
each_zipped(iter1, iter2, |val1, val2| {
let val1 = *val1.unwrap();
let val2 = *val2.unwrap();
println!("{}, {}", val1, val2);
(val1 < 10, val2 < 10)
});
}
答案 1 :(得分:1)
此代码的问题在于RefIter
以两种方式使用,这两种方式基本上相互矛盾:
next_ref
的来电者接受对存储值的引用,该存储值与RefIter
RefIter
的值必须是可变的,以便每次调用都可以递增这完美地描述了可变别名(您正在尝试修改&#39;值&#39;同时保留对它的引用) - Rust明确设计用来防止这种情况。
为了使each_zipped
有效,您需要修改RefIter
以避免分发对您希望变异的数据的引用。
我使用RefCell
和Rc
的组合在下面实现了一种可能性:
use std::cell::RefCell;
use std::rc::Rc;
// An iterator-like type, that returns references to itself
// in next_ref
struct RefIter {
value: RefCell<Rc<u64>>
}
impl RefIter {
fn next_ref(&self) -> Option<Rc<u64>> {
let new_val = Rc::new(**self.value.borrow() + 1);
*self.value.borrow_mut() = new_val;
Some(Rc::clone(&*self.value.borrow()))
}
}
// Iterate over two RefIter simultaniously and call a callback
// for each pair. The callback returns a tuple of bools
// that indicate which iterators should be advanced.
fn each_zipped<F>(iter1: RefIter, iter2: RefIter, callback: F)
where F: Fn(&Option<Rc<u64>>, &Option<Rc<u64>>) -> (bool, bool)
{
let mut current1 = iter1.next_ref();
let mut current2 = iter2.next_ref();
loop {
let advance_flags = callback(¤t1, ¤t2);
match advance_flags {
(true, true) => {
current1 = iter1.next_ref();
current2 = iter2.next_ref();
},
(true, false) => {
current1 = iter1.next_ref();
},
(false, true) => {
current2 = iter1.next_ref();
},
(false, false) => {
return
}
}
}
}
fn main() {
let iter1 = RefIter { value: RefCell::new(Rc::new(3)) };
let iter2 = RefIter { value: RefCell::new(Rc::new(4)) };
each_zipped(iter1, iter2, |val1, val2| {
// We can't use unwrap() directly, since we're only passed a reference to an Option
let val1 = **val1.iter().next().unwrap();
let val2 = **val2.iter().next().unwrap();
println!("{}, {}", val1, val2);
(val1 < 10, val2 < 10)
});
}
此RefIter
版本向消费者发送Rc
,而不是参考。这避免了可变别名的问题 - 通过放置来更新value
外部Rc
中的新RefCell
。这样做的一个副作用是消费者能够抓住一个“老年人”。即使在Rc
已经提升之后,也会引用缓冲区(通过返回的RefIter
)。