Krish
Krish

Reputation: 1081

Range where start > end

for x in line.x1..=line.x2 {
    ...
}

This doesn't work for cases where x1 > x2, so I use this workaround:

for x in (cmp::min(line.x1, line.x2))..=(cmp::max(line.x1, line.x2)) {
    ...
}

This was fine until I needed to iterate through two fields in tandem:

for (x, y) in (line.x1..=line.x2).zip((line.y1..=line.y2)) {
    ...
}

Here my previous trick cannot work.

Is there an idiomatic way to use ranges where the start value may be greater than the end value?


Solution based on Brian's answer:

fn range_inclusive(a: usize, b: usize) -> impl Iterator<Item = usize> {
    let x: Box<dyn Iterator<Item = usize>>;
    if b > a {
        x = Box::new(a..=b)
    } else {
        x = Box::new((b..=a).rev())
    }
    x
}

fn main() {
    for i in range_inclusive(3, 1).zip(range_inclusive(1, 3)) {
        println!("{:?}", i);
    }
}

Upvotes: 3

Views: 2480

Answers (2)

Brian Reischl
Brian Reischl

Reputation: 7356

I don't know if this is really better than the snippet you posted in the other comment thread, but this may also work depending on your circumstances.

Edit: the following code includes refinements from @Krish. See also his playground for it.

fn range_inclusive(a: usize, b: usize) -> impl Iterator<Item = usize> {
    let x: Box<dyn Iterator<Item = usize>>;
    if b > a {
        x = Box::new(a..=b)
    } else {
        x = Box::new((b..=a).rev())
    }
    x
}

For posterity, here's what I had originally posted, prior to Krish's improvements.

fn get_range_iter_inclusive(a: usize, b: usize) -> impl Iterator<Item = usize> {
    if b > a {
        let vec: Vec<usize> = (a..=b).collect();
        vec.into_iter()
    } else {
        let vec: Vec<usize> = (b..=a).rev().collect();
        vec.into_iter()
    }
}

Materializing the ranges into Vec<_> is obviously bad for runtime performance in some cases. This was the only way I could figure out to beat the type system into submission. But I'm just learning Rust, so there could very well be a better way.

As an aside, I'm still struggling to understand why creating a descending range should not only fail, but fail silently.

Upvotes: 1

Jeremy Meadows
Jeremy Meadows

Reputation: 2561

The simplest way would be to reverse the range that you need:

for i in (0..11).rev() {
    println!("{}", i);
}

will print 10 to 0 in decreasing order.

Upvotes: 4

Related Questions