这个有可能。正如您所确定的,您需要从两个句柄对基本迭代器进行可变引用,这可以使用具有“内部可变性”的类型 http://doc.rust-lang.org/master/std/cell/,即使用unsafe
内部代码公开一个安全的API来获取&mut
可别名数据(即包含在&
)通过动态强制编译器通常在外部编译时强制执行的不变量unsafe
.
I'm assuming you're happy to keep the two iterators on a single thread1, so, in this case, we want a RefCell http://doc.rust-lang.org/std/cell/struct.RefCell.html. We also need to be able to have access to the RefCell
from the two handles, entailing storing either a &RefCell<...>
or an Rc<RefCell<...>>
. The former would be too restrictive, as it would only allow us to use the pair of iterators in and below the stack frame in which the RefCell
is created, while we want to be able to freely pass the iterators around, so Rc http://doc.rust-lang.org/std/rc/index.html it is.
总之,我们基本上将存储一个Rc<RefCell<Iterator<(A,B)>>>
,这只是缓冲的问题。适合这里工作的工具是RingBuf http://doc.rust-lang.org/std/collections/struct.RingBuf.html因为我们希望在前面和后面进行有效的推/弹出操作。因此,我们正在共享的东西(即在RefCell
)可能看起来像:
struct SharedInner<A, B, It> {
iter: It,
first: RingBuf<A>,
second: RingBuf<B>,
}
我们可以将实际共享的类型缩写为type Shared<A, B, It> = Rc<RefCell<SharedInner<A, B, It>>>;
,它允许我们定义迭代器:
struct First<A, B, It> {
data: Shared<A, B, It>
}
impl Iterator<A> for First<A,B,It> {
fn next(&mut self) -> Option<A> {
// ...
}
}
实施next
首先要做的是得到一个&mut
to the SharedInner
, via self.data.borrow_mut();
。然后从中获取一个元素:检查正确的缓冲区,或者从其中获取一个新元素iter
(记得缓冲剩余的B
):
let mut inner = self.data.borrow_mut();
inner.first.pop_front().or_else(|| {
inner.iter.next().map(|(a,b)| {
inner.second.push(b);
a
})
})
Docs: RingBuf.pop_front http://doc.rust-lang.org/std/collections/struct.RingBuf.html#method.pop_front, Option.or_else http://doc.rust-lang.org/core/option/type.Option.html#method.or_else.
另一侧的迭代器类似。总共:
use std::cell::RefCell;
use std::collections::{Deque, RingBuf};
use std::rc::Rc;
struct SharedInner<A, B, It> {
iter: It,
first: RingBuf<A>,
second: RingBuf<B>
}
type Shared<A, B, It> = Rc<RefCell<SharedInner<A, B, It>>>;
struct First<A, B, It> {
data: Shared<A, B, It>
}
impl<A,B, It: Iterator<(A,B)>> Iterator<A> for First<A, B, It> {
fn next(&mut self) -> Option<A> {
let mut inner = self.data.borrow_mut();
// try to get one from the stored data
inner.first.pop_front().or_else(||
// nothing stored, we need a new element.
inner.iter.next().map(|(a, b)| {
inner.second.push(b);
a
}))
}
}
struct Second<A, B, It> {
data: Shared<A, B, It>
}
impl<A,B, It: Iterator<(A,B)>> Iterator<B> for Second<A,B,It> {
fn next(&mut self) -> Option<B> {
let mut inner = self.data.borrow_mut();
inner.second.pop_front().or_else(|| {
inner.iter.next().map(|(a, b)| {
inner.first.push(a);
b
})
})
}
}
fn split<A, B, It: Iterator<(A,B)>>(it: It) -> (First<A, B, It>,
Second<A, B, It>) {
let data = Rc::new(RefCell::new(SharedInner {
iter: it,
first: RingBuf::new(),
second: RingBuf::new(),
}));
(First { data: data.clone() }, Second { data: data })
}
fn main() {
let pairs = range(1u32, 10 + 1).map(|x| (x, 1.0 / x as f64));
let (mut first, mut second) = split(pairs);
println!("first:");
for x in first.by_ref().take(3) {
println!(" {}", x);
}
println!("second:");
for y in second.by_ref().take(5) {
if y < 0.2 { break }
println!(" {}", y);
}
let a = first.collect::<Vec<u32>>();
let b = second.collect::<Vec<f64>>();
println!("a {}\nb {}", a, b);
}
打印
first:
1
2
3
second:
1
0.5
0.333333
0.25
0.2
a [4, 5, 6, 7, 8, 9, 10]
b [0.166667, 0.142857, 0.125, 0.111111, 0.1]
playpen http://is.gd/U1qj6n.
有多种方法可以对此进行优化,例如取入时First
,仅缓冲剩余的B
if a Second
句柄存在。
1 If you were looking to run them in separate threads just replace the RefCell
with a Mutex http://doc.rust-lang.org/std/sync/struct.Mutex.html and the Rc
with an Arc http://doc.rust-lang.org/std/sync/struct.Arc.html, and add the necessary bounds.