Parece-me que até que o .step_bymétodo se torne estável, pode-se facilmente realizar o que você deseja com um Iterator(que é o que Rangerealmente é de qualquer maneira):
struct SimpleStepRange(isize, isize, isize);
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);
}
}
Se for necessário iterar vários intervalos de diferentes tipos, o código pode ser genérico da seguinte maneira:
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);
}
}
Vou deixar para você eliminar a verificação de limites superiores para criar uma estrutura aberta se um loop infinito for necessário ...
A vantagem dessa abordagem é que funciona com foradição de açúcar e continuará a funcionar mesmo quando recursos instáveis se tornarem utilizáveis; Além disso, ao contrário da abordagem sem açúcar usando os padrões Range, ele não perde eficiência com várias .next()chamadas. As desvantagens são que são necessárias algumas linhas de código para configurar o iterador, portanto, pode valer a pena apenas para código que tem muitos loops.