如何组成可变迭代器?

时间:2015-05-08 17:09:08

标签: iterator rust immutability lifetime

  

编者注:此代码示例来自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
        }        
    }
}

我已经玩弄了各种变化,但我似乎无法让生活和类型相匹配。现在编译器告诉我

  1. 我无法改变self.base
  2. 变量素数不够长
  3. 这是我得到的错误

    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会让我这样做?

1 个答案:

答案 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模型缺失OptionOption::takeNone替换它所调用的值,并返回那里的值。这在对非可复制对象进行混洗时非常有用。

但请注意,此筛选实现将是内存和计算效率低下 - 对于每个素数,您要创建一个额外的迭代器层,这需要占用堆空间。调用next()时堆栈的深度也会随着素数的增加呈线性增长,因此在足够大的数字上会出现堆栈溢出:

fn main() {
    println!("{}", Primes::new(2..).nth(10000).unwrap());
}

运行它:

% ./test1 

thread '<main>' has overflowed its stack
zsh: illegal hardware instruction (core dumped)  ./test1