如何使用自定义步骤在范围内进行迭代?


100

如何用1以外的步骤迭代Rust中的某个范围?我来自C ++背景,所以我想做些类似的事情

for(auto i = 0; i <= n; i+=2) {
    //...
}

在Rust中,我需要使用该range函数,而且似乎没有第三个参数可用于执行自定义步骤。我该怎么做?

Answers:



12

在我看来,在.step_by方法变得稳定之前,您可以轻松地通过使用Iterator(这Range实际上是真正的)完成您想要的事情:

struct SimpleStepRange(isize, isize, isize);  // start, end, and step

impl Iterator for SimpleStepRange {
    type Item = isize;

    #[inline]
    fn next(&mut self) -> Option<isize> {
        if self.0 < self.1 {
            let v = self.0;
            self.0 = v + self.2;
            Some(v)
        } else {
            None
        }
    }
}

fn main() {
    for i in SimpleStepRange(0, 10, 2) {
        println!("{}", i);
    }
}

如果需要迭代多个不同类型的范围,则可以使代码通用,如下所示:

use std::ops::Add;

struct StepRange<T>(T, T, T)
    where for<'a> &'a T: Add<&'a T, Output = T>,
          T: PartialOrd,
          T: Clone;

impl<T> Iterator for StepRange<T>
    where for<'a> &'a T: Add<&'a T, Output = T>,
          T: PartialOrd,
          T: Clone
{
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<T> {
        if self.0 < self.1 {
            let v = self.0.clone();
            self.0 = &v + &self.2;
            Some(v)
        } else {
            None
        }
    }
}

fn main() {
    for i in StepRange(0u64, 10u64, 2u64) {
        println!("{}", i);
    }
}

如果需要无限循环,我将留给您消除上限检查以创建开放式结构...

这种方法的优点是可以for加糖,即使不稳定的功能可用也可以继续使用。而且,与使用标准Ranges的消减方法不同,它不会因多次.next()调用而失去效率。缺点是设置迭代器需要花费几行代码,因此仅对于具有很多循环的代码才值得。


通过添加其他类型,U您可以在第二个选项中使用支持其他类型加法的类型,但仍会产生a T。例如,时间和持续时间浮现在脑海。
瑞安

@Ryan,这似乎是个好主意,应该可以工作,结构定义如下:struct StepRange <T>(T,T,U)其中for <'a,'b>&'a T:Add <&' b U,输出= T>,T:PartialOrd,T:克隆;对于输入的T和U类型,应该允许不同的寿命。
GordonBGood


3

您将编写C ++代码:

for (auto i = 0; i <= n; i += 2) {
    //...
}

...在Rust中是这样的:

let mut i = 0;
while i <= n {
    // ...
    i += 2;
}

我认为Rust版本也更具可读性。


回复:我认为,在循环中插入“ continue”,即使在for结构中也只能在条件分支内执行此操作。如果是这样,那么我认为可以在“继续”之前在while结构中的条件分支内增加,然后它将按预期工作。还是我忽略了什么?
WDS

1
@WDS是违反直觉的工作,以获得语言的基本功能continue,以使其正常工作。尽管可以做到,但这种设计会鼓励错误。
Chai T. Rex

2

如果您要逐步执行一些预定义的操作(例如2),则可能希望使用迭代器手动执行操作。例如:

let mut iter = 1..10;
loop {
    match iter.next() {
        Some(x) => {
            println!("{}", x);
        },
        None => break,
    }
    iter.next();
}

您甚至可以用它随意增加一个步长(尽管这肯定会变得越来越长且更难消化):

let mut iter = 1..10;
let step = 4;
loop {
    match iter.next() {
        Some(x) => {
            println!("{}", x);
        },
        None => break,
    }
    for _ in 0..step-1 {
        iter.next();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.