Rust确实有生成器,但它们是高度实验性的,目前在稳定的 Rust 中不可用。
适用于稳定的 Rust 1.0 及更高版本
Range
处理你的具体例子。您可以将它与以下语法糖一起使用..
:
fn main() {
let sum: u64 = (0..1_000_000).sum();
println!("{}", sum)
}
如果Range
不存在呢?我们可以创建一个对其建模的迭代器!
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)
}
胆量是相同的,但比 Python 版本更明确。值得注意的是,Python 的生成器会为您跟踪状态。Rust 更喜欢明确性,因此我们必须创建自己的状态并手动更新它。重要的部分是Iterator
trait的实现。type Item = u64
我们指定迭代器产生特定类型的值(
这个例子没有真正的强大Range
,它使用泛型,但展示了一个如何去做的例子。
在夜间 Rust 中工作
Nightly Rust确实有生成器,但它们是高度实验性的。您需要引入一些不稳定的功能来创建一个。但是,它看起来非常接近 Python 示例,并添加了一些特定于 Rust 的内容:
// 1.43.0-nightly (2020-02-09 71c7e149e42cb0fc78a8)
#![feature(generators, generator_trait)]
use std::{
ops::{Generator, GeneratorState},
pin::Pin,
};
fn firstn(n: u64) -> impl Generator<Yield = u64, Return = ()> {
move || {
let mut num = 0;
while num < n {
yield num;
num += 1;
}
}
}
由于当前 Rust 中的所有内容都在迭代器上运行,因此我们创建了一个适配器,将生成器转换为迭代器,以便与更广泛的生态系统一起玩。我希望这样的适配器最终会出现在标准库中:
struct GeneratorIteratorAdapter<G>(Pin<Box<G>>);
impl<G> GeneratorIteratorAdapter<G>
where
G: Generator<Return = ()>,
{
fn new(gen: G) -> Self {
Self(Box::pin(gen))
}
}
impl<G> Iterator for GeneratorIteratorAdapter<G>
where
G: Generator<Return = ()>,
{
type Item = G::Yield;
fn next(&mut self) -> Option<Self::Item> {
match self.0.as_mut().resume(()) {
GeneratorState::Yielded(x) => Some(x),
GeneratorState::Complete(_) => None,
}
}
}
现在我们可以使用它了:
fn main() {
let generator_iterator = GeneratorIteratorAdapter::new(firstn(1_000_000));
let sum: u64 = generator_iterator.sum();
println!("{}", sum);
}
有趣的是,它不如Iterator
. 例如,迭代器有一个size_hint
方法,它允许迭代器的使用者知道剩余多少元素。这允许在collect
进入容器时进行优化。生成器没有任何此类信息。