Es scheint mir, dass man, bis die .step_by
Methode stabil ist, leicht erreichen kann, was man will mit einem Iterator
(was Range
eigentlich sowieso ist):
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);
}
}
Wenn mehrere Bereiche unterschiedlichen Typs iteriert werden müssen, kann der Code wie folgt generisch gestaltet werden:
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);
}
}
Ich überlasse es Ihnen, die Prüfung der oberen Grenzen zu eliminieren, um eine Struktur mit offenem Ende zu erstellen, wenn eine Endlosschleife erforderlich ist ...
Der Vorteil dieses Ansatzes besteht darin, dass er mit for
Zucker arbeitet und auch dann weiter funktioniert, wenn instabile Funktionen verwendet werden können. Im Gegensatz zum Ansatz ohne Zucker, bei dem die Standards verwendet werden Range
, geht die Effizienz bei mehreren .next()
Anrufen nicht verloren . Nachteile sind, dass zum Einrichten des Iterators einige Codezeilen erforderlich sind. Dies lohnt sich möglicherweise nur für Code mit vielen Schleifen.