Rust tut Generatoren haben, aber sie sind höchst experimentell und derzeit nicht in stabilen Rust.
Arbeiten in stabilen Rust 1.0 und höher
Range
Griffe Ihr konkretes Beispiel. Sie können es mit dem syntaktischen Zucker von ..
verwenden:
fn main() {
let sum: u64 = (0..1_000_000).sum();
println!("{}", sum)
}
Was passiert, wenn Range
nicht existieren? Wir können einen Iterator erstellen, der es modelliert!
struct MyRange {
start: u64,
end: u64,
}
impl MyRange {
fn new(start: u64, end: u64) -> MyRange {
MyRange {
start: start,
end: end,
}
}
}
impl Iterator for MyRange {
type Item = u64;
fn next(&mut self) -> Option<u64> {
if self.start == self.end {
None
} else {
let result = Some(self.start);
self.start += 1;
result
}
}
}
fn main() {
let sum: u64 = MyRange::new(0, 1_000_000).sum();
println!("{}", sum)
}
Die Eingeweide sind die gleichen, aber expliziter als die Python-Version. Bemerkenswert ist, dass Pythons Generatoren den Status für Sie verfolgen. Rust bevorzugt Explizitheit, also müssen wir unseren eigenen Status erstellen und manuell aktualisieren. Der wichtige Teil ist die Implementierung der Iterator
trait. Wir geben an, dass der Iterator Werte eines bestimmten Typs liefert (type Item = u64
) und dann mit dem schrittweisen Ausführen jeder Iteration und dem Ermitteln, dass wir das Ende der Iteration erreicht haben.
Dieses Beispiel ist nicht so leistungsfähig wie das echte Range
, das Generics verwendet, aber zeigt ein Beispiel dafür, wie es geht.
Werke in der nächtlichen Rust
Nightly Rust does have generators, aber sie sind höchst experimentell. Sie müssen ein paar instabile Funktionen einbringen, um eines zu erstellen. Allerdings sieht es ziemlich nahe dem Python Beispiel mit einigen Rust spezifische Ergänzungen:
#![feature(generators, generator_trait, conservative_impl_trait)]
use std::ops::{Generator, GeneratorState};
fn firstn(n: u64) -> impl Generator<Yield = u64, Return =()> {
move || {
let mut num = 0;
while num < n {
yield num;
num += 1;
}
}
}
Da alles in der aktuellen Rust auf Iteratoren arbeitet, schaffen wir einen Adapter, der einen Generator in einem Iterator, um umwandelt mit dem breiteren Ökosystem spielen.Ich würde erwarten, dass ein solcher Adapter in der Standard-Bibliothek vorhanden sein würde schließlich:
struct GeneratorIteratorAdapter<G>(G);
impl<G> Iterator for GeneratorIteratorAdapter<G>
where
G: Generator<Return =()>,
{
type Item = G::Yield;
fn next(&mut self) -> Option<Self::Item> {
match self.0.resume() {
GeneratorState::Yielded(x) => Some(x),
GeneratorState::Complete(_) => None,
}
}
}
Jetzt können wir sie verwenden:
fn main() {
let generator_iterator = GeneratorIteratorAdapter(firstn(1_000_000));
let sum: u64 = generator_iterator.sum();
println!("{}", sum);
}
Was ist das interessant ist, dass es weniger leistungsfähig als eine Implementierung Iterator
. Zum Beispiel haben Iteratoren die size_hint
-Methode, die es Verbrauchern des Iterators ermöglicht, eine Vorstellung davon zu haben, wie viele Elemente noch übrig sind. Dies ermöglicht Optimierungen, wenn Sie in einen Container einsteigen. Generatoren haben keine solchen Informationen.
auf die genaue Reihenfolge Je gibt es mehrere Build-in-Iteratoren, die verwendet werden könnten, z.B [Unfolder] (http://static.rust-lang.org/doc/core/iterator.html#struct-unfoldyrator) oder ['Counter'] (http://static.rust-lang.org/ doc/core/iterator.html # struct-counter) plus ein ['scan'] (http://static.rust-lang.org/doc/core/iterator.html#method-scan) (und/oder ein beliebiges von die anderen Kombinationsfunktionen. (Leider gibt es keine Dokumentation für die anderen als die Typen.) – huon
Ich frage mich, ob es möglich ist, ein Makro zu schreiben, mit dem Sie benutzerdefinierte Iteratorstrukturen definieren können, aber mit etwas weniger Vortexen. – eremzeit