标准库代码位于https://github.com/rust-lang/rust/tree/master/library
这里使用目前最新版本1.45.2
概述
生成器是rust协程(异步编程)实现的基础,async代码最终会编译成生成器,一个生成器就是一个可恢复函数,生成器和闭包比较相似,但在编译器中会将生成器编译成截然不同的语义。生成器最大的特点就是,程序的执行流程可以在生成器和调用者之间来回切换。当我们需要暂时从生成器中返回的时候,就使用yield
关键字;当调用者希望再次进入生成器的时候,就调用resume
方法,这时程序执行的流程是从上次yield返回的那个点继续执行 ,它可以保证代码不会重复执行,恢复执行时能获取到上一次变量的值。
标准库
std::ops::{Generator, GeneratorState}
src/libcore/ops/generator.rs
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
#[lang = "generator_state"]
pub enum GeneratorState<Y, R> {
Yielded(Y),
Complete(R),
}
#[lang = "generator"]
#[fundamental]
pub trait Generator<R = ()> {
type Yield;
type Return;
fn resume(self: Pin<&mut Self>, arg: R) -> GeneratorState<Self::Yield, Self::Return>;
}
可以看到标准库提供的enum和trait分别用generator_state和generator标识,这说明编译器会做特殊处理。
和闭包的关系
一个简单的生成器例子,可以看出来和闭包没什么不同,就多了一个闭包里没见过的yield关键词
#![feature(generators, generator_trait)]
use std::ops::{Generator, GeneratorState};
use std::pin::Pin;
fn main() {
let mut generator = || {
yield 1;
return "foo"
};
match Pin::new(&mut generator).resume(()) {
GeneratorState::Yielded(1) => {}
_ => panic!("unexpected value from resume"),
}
match Pin::new(&mut generator).resume(()) {
GeneratorState::Complete("foo") => {}
_ => panic!("unexpected value from resume"),
}
}
- 生成器和闭包类似,只不过多了一个
yield
关键词 - 可以多次调用resume ,这又和迭代器类似(惰性)
- 和闭包一样可以捕获外部环境的变量,也可以使用move关键字将变量所有权移动到生成器中。每当生成器被drop时,它将drop掉所有捕获的环境变量。
- 生成器自动实现了Send和Sync,但不会自动实现Copy或Clone之类的trait
生成的代码究竟是啥样?
在编译器中,生成器编译成状态机。每个yield
表达式对应一个不同的状态.
再看一个例子
#![feature(generators, generator_trait)]
use std::ops::Generator;
use std::pin::Pin;
fn main() {
let ret = "foo";
let mut generator = move || {
yield 1;
return ret
};
Pin::new(&mut generator).resume(());
Pin::new(&mut generator).resume(());
}
上面的例子编译器翻译后的代码大概是这样子:
#![feature(arbitrary_self_types, generators, generator_trait)]
use std::ops::{Generator, GeneratorState};
use std::pin::Pin;
fn main() {
let ret = "foo";
let mut generator = {
enum __Generator {
Start(&'static str),
Yield1(&'static str),
Done,
}
impl Generator for __Generator {
type Yield = i32;
type Return = &'static str;
fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {
use std::mem;
match mem::replace(&mut *self, __Generator::Done) {
__Generator::Start(s) => {
*self = __Generator::Yield1(s);
GeneratorState::Yielded(1)
}
__Generator::Yield1(s) => {
*self = __Generator::Done;
GeneratorState::Complete(s)
}
__Generator::Done => {
panic!("generator resumed after completion")
}
}
}
}
__Generator::Start(ret)
};
Pin::new(&mut generator).resume(());
Pin::new(&mut generator).resume(());
}
编译器生成器相关源码:
src/librustc_mir/transform/generator.rs
src/librustc_ast_lowering/expr.rs