Me parece que hasta que el .step_by
método se estabilice, uno puede lograr fácilmente lo que desea con un Iterator
(que es lo que Range
realmente son de todos modos):
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);
}
}
Si uno necesita iterar múltiples rangos de diferentes tipos, el código puede hacerse genérico de la siguiente manera:
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);
}
}
Dejaré que usted elimine la verificación de límites superiores para crear una estructura abierta si se requiere un bucle infinito ...
Las ventajas de este enfoque es que funciona con el for
azúcar y seguirá funcionando incluso cuando las características inestables se vuelvan utilizables; Además, a diferencia del enfoque sin azúcar que usa los estándares Range
, no pierde eficiencia por múltiples .next()
llamadas. Las desventajas son que se necesitan algunas líneas de código para configurar el iterador, por lo que solo puede valer la pena para el código que tiene muchos bucles.