ruggine fa hanno generatori, ma sono altamente sperimentale e non attualmente disponibile in ruggine stabile.
Lavori in stabile Rust 1.0 e superiori
Range
gestisce il vostro esempio concreto. Si può usare con lo zucchero sintattico di ..
:
fn main() {
let sum: u64 = (0..1_000_000).sum();
println!("{}", sum)
}
E se Range
non esistesse? Possiamo creare un iteratore che lo modella!
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)
}
Gli intestini sono gli stessi, ma più espliciti rispetto alla versione Python. In particolare, i generatori di Python tengono traccia dello stato per te. La ruggine preferisce l'explicitness, quindi dobbiamo creare il nostro stato e aggiornarlo manualmente. La parte importante è l'implementazione di Iterator
trait. Specifichiamo che l'iteratore restituisce valori di un tipo specifico (type Item = u64
) e quindi gestiamo l'stepping di ciascuna iterazione e il modo in cui dire che abbiamo raggiunto la fine dell'iterazione.
Questo esempio non è potente come il reale Range
, che utilizza i generici, ma mostra un esempio di come procedere.
Lavori in Rust notte
Nightly Rust does have generators, ma sono altamente sperimentale. Devi creare alcune caratteristiche instabili per crearne una. Tuttavia, sembra abbastanza vicino l'esempio di Python, con alcune aggiunte Rust-specifici:
#![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;
}
}
}
Poiché tutto a Rust attuale opera su iteratori, creiamo un adattatore che converte un generatore in un iteratore al fine di giocare con l'ecosistema più ampio.Mi aspetto che un tale adattatore sarebbe presente nella libreria standard alla fine:
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,
}
}
}
Ora si può usare:
fn main() {
let generator_iterator = GeneratorIteratorAdapter(firstn(1_000_000));
let sum: u64 = generator_iterator.sum();
println!("{}", sum);
}
La cosa interessante di questo è che è meno potente di un'implementazione di Iterator
. Ad esempio, gli iteratori hanno il metodo size_hint
, che consente ai consumatori dell'iteratore di avere un'idea di quanti elementi restano. Ciò consente ottimizzazioni quando collect
si inserisce in un contenitore. I generatori non hanno tali informazioni.
seconda della esatta sequenza, ci sono diversi iteratori build-in che potrebbero essere utilizzati per esempio ['Unfoldr'] (http://static.rust-lang.org/doc/core/iterator.html#struct-unfoldriterator) o [' Counter'] (http://static.rust-lang.org/ doc/core/iterator.html # struct-counter) più un ['scan'] (http://static.rust-lang.org/doc/core/iterator.html#method-scan) (e/o uno qualsiasi di l'altra che combina le funzioni. (Sfortunatamente non esiste una documentazione per quelli diversi dai tipi ancora.) – huon
Mi chiedo se sia possibile scrivere una macro che ti permetta di definire le strutture iterative personalizzate ma con un po 'meno di boilerplate. – eremzeit