Reputation: 3616
I'd ideally like to have something like the following:
iter = if go_up {
(min .. limit)
} else {
(limit .. max).rev()
};
to create an iterator that either counts up or down to some limit, depending on the situation. However, because Range
and Rev
are different types, I can't do this. I can use the step_by
feature, but because my limits are an unsigned data-type, I then also have to cast everything. The best I have so far is:
#![feature(step_by)]
iter = if go_up {
(min as i64 .. limit as i64).step_by(1)
} else {
(limit as i64 .. max as i64).step_by(-1)
};
but this requires both unstable features, and shoehorning my types. It seems like there should be a neater way to do this; does anyone know one?
Upvotes: 1
Views: 164
Reputation: 60147
Personally, your solution
iter = if go_up {
(min as i64 .. limit as i64).step_by(1)
} else {
(limit as i64 .. max as i64).step_by(-1)
};
is a better option than Shepmaster's first example, since it's more complete (eg. there's a size_hint
), it's more likely to be correct by virtue of being a standard tool and it's faster to write.
It's true that this is unstable, but there's nothing stopping you from just copying the source in the meantime. That gives you a nice upgrade path for when this eventually gets stabilized.
The enum
wrapper technique is great in more complex cases, though, but in this case I'd be tempted to KISS.
Upvotes: 2
Reputation: 431011
The direct solution is to simply create an iterator that can either count upwards or downwards. Use an enum
to choose between the types:
use std::ops::Range;
use std::iter::Rev;
enum Foo {
Upwards(Range<u8>),
Downwards(Rev<Range<u8>>),
}
impl Foo {
fn new(min: u8, limit: u8, max: u8, go_up: bool) -> Foo {
if go_up {
Foo::Upwards(min..limit)
} else {
Foo::Downwards((limit..max).rev())
}
}
}
impl Iterator for Foo {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
match *self {
Foo::Upwards(ref mut i) => i.next(),
Foo::Downwards(ref mut i) => i.next(),
}
}
}
fn main() {
for i in Foo::new(1, 5, 10, true) {
println!("{}", i);
}
for i in Foo::new(1, 5, 10, false) {
println!("{}", i);
}
}
Another pragmatic solution that introduces a little bit of indirection is to Box
the iterators:
fn thing(min: u8, limit: u8, max: u8, go_up: bool) -> Box<Iterator<Item = u8>> {
if go_up {
Box::new(min..limit)
} else {
Box::new((limit..max).rev())
}
}
fn main() {
for i in thing(1, 5, 10, true) {
println!("{}", i);
}
for i in thing(1, 5, 10, false) {
println!("{}", i);
}
}
Upvotes: 4