编者注:此代码示例来自1.0之前的Rust版本,并且在语法上不是有效的Rust 1.0代码。此代码的更新版本会产生不同的错误,但答案仍包含有价值的信息。
我想创建一个生成素数流的迭代器。我的一般思维过程是使用连续的过滤器包装迭代器,例如,以
开头let mut n = (2..N)
然后对于每个素数,你改变迭代器并添加一个过滤器
let p1 = n.next()
n = n.filter(|&x| x%p1 !=0)
let p2 = n.next()
n = n.filter(|&x| x%p2 !=0)
我正在尝试使用以下代码,但我似乎无法让它工作
struct Primes {
base: Iterator<Item = u64>,
}
impl<'a> Iterator for Primes<'a> {
type Item = u64;
fn next(&mut self) -> Option<u64> {
let p = self.base.next();
match p {
Some(n) => {
let prime = n.clone();
let step = self.base.filter(move |&: &x| {x%prime!=0});
self.base = &step as &Iterator<Item = u64>;
Some(n)
},
_ => None
}
}
}
我已经玩弄了各种变化,但我似乎无法让生活和类型相匹配。现在编译器告诉我
这是我得到的错误
solution.rs:16:17: 16:26 error: cannot borrow immutable borrowed content `*self.base` as mutable
solution.rs:16 let p = self.base.next();
^~~~~~~~~
solution.rs:20:28: 20:37 error: cannot borrow immutable borrowed content `*self.base` as mutable
solution.rs:20 let step = self.base.filter(move |&: &x| {x%prime!=0});
^~~~~~~~~
solution.rs:21:30: 21:34 error: `step` does not live long enough
solution.rs:21 self.base = &step as &Iterator<Item = u64>;
^~~~
solution.rs:15:39: 26:6 note: reference must be valid for the lifetime 'a as defined on the block at 15:38...
solution.rs:15 fn next(&mut self) -> Option<u64> {
solution.rs:16 let p = self.base.next();
solution.rs:17 match p {
solution.rs:18 Some(n) => {
solution.rs:19 let prime = n.clone();
solution.rs:20 let step = self.base.filter(move |&: &x| {x%prime!=0});
...
solution.rs:20:71: 23:14 note: ...but borrowed value is only valid for the block suffix following statement 1 at 20:70
solution.rs:20 let step = self.base.filter(move |&: &x| {x%prime!=0});
solution.rs:21 self.base = &step as &Iterator<Item = u64>;
solution.rs:22 Some(n)
solution.rs:23 },
error: aborting due to 3 previous errors
为什么赢得Rust会让我这样做?
答案 0 :(得分:7)
这是一个工作版本:
struct Primes<'a> {
base: Option<Box<Iterator<Item = u64> + 'a>>,
}
impl<'a> Iterator for Primes<'a> {
type Item = u64;
fn next(&mut self) -> Option<u64> {
let p = self.base.as_mut().unwrap().next();
p.map(|n| {
let base = self.base.take();
let step = base.unwrap().filter(move |x| x % n != 0);
self.base = Some(Box::new(step));
n
})
}
}
impl<'a> Primes<'a> {
#[inline]
pub fn new<I: Iterator<Item = u64> + 'a>(r: I) -> Primes<'a> {
Primes {
base: Some(Box::new(r)),
}
}
}
fn main() {
for p in Primes::new(2..).take(32) {
print!("{} ", p);
}
println!("");
}
我正在使用Box<Iterator>
特征对象。拳击是不可避免的,因为内部迭代器必须存储在next()
个调用之间的某个地方,并且你无处可存储参考特征对象。
我将内部迭代器设为Option
。这是必要的,因为您需要将其替换为消耗它的值,因此内部迭代器可能会在短时间内从结构中“缺失”。 Rust模型缺失Option
。 Option::take
用None
替换它所调用的值,并返回那里的值。这在对非可复制对象进行混洗时非常有用。
但请注意,此筛选实现将是内存和计算效率低下 - 对于每个素数,您要创建一个额外的迭代器层,这需要占用堆空间。调用next()
时堆栈的深度也会随着素数的增加呈线性增长,因此在足够大的数字上会出现堆栈溢出:
fn main() {
println!("{}", Primes::new(2..).nth(10000).unwrap());
}
运行它:
% ./test1
thread '<main>' has overflowed its stack
zsh: illegal hardware instruction (core dumped) ./test1