Generator

#rust #generator #pin #coroutine

generator nightly flags #

  • #![feature(generators, generator_trait)]

generator trait #

源码 https://github.com/rust-lang/rust/blob/master/library/core/src/ops/generator.rs

pub enum GeneratorState<Y, R> {
    Yielded(Y),
    Complete(R),
}

pub trait Generator<R = ()> {
    type Yield;
    type Return;
    fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;
}

使用 #

fn test_g1() {
    let mut g = || {
        yield "hi";
        ()
    };

    while let GeneratorState::Yielded(value) = Pin::new(&mut g).resume(()) {
        println!("value: {value}");
    }
}

上面的例子并没提现 resume 时候的参数。参考下面的例子:

fn test_g2() {

    // return type 用到了 never_type, #![feature(never_type)]
    fn map<T, U>(mut f: impl FnMut(T) -> U) -> impl Generator<T, Yield=U, Return=!> + Unpin {
        move |mut t| loop {
            t = yield f(t);
        }
    }
    
    let mut gen = Box::pin(map(|x| x * 2));
    dbg!(gen.as_mut().resume(1));
    dbg!(gen.as_mut().resume(2));
    dbg!(gen.as_mut().resume(3));
}

在自己的类型上实现 generator trait #

fn test_gen_impl() {
    enum GeneratorA {
        Enter,
        Counter { state: u8 },
        Exit,
    }

    impl GeneratorA {
        fn new() -> Self { GeneratorA::Enter } // created in `Enter` state
    }

    impl ! Unpin for GeneratorA {}

    // 类型参数 R 实例到 u32 上
    impl Generator<u32> for GeneratorA {
        type Yield = u8;
        type Return = ();

        fn resume(mut self: Pin<&mut Self>, limit: u32) -> GeneratorState<Self::Yield, Self::Return> {
            let this = unsafe { self.get_unchecked_mut() };
            match *this {
                GeneratorA::Enter => {
                    *this = GeneratorA::Counter { state: 0 };
                    GeneratorState::Yielded(0)
                }
                GeneratorA::Counter { state } => {
                    if state == limit as u8 {
                        GeneratorState::Complete(())
                    } else {
                        *this = GeneratorA::Counter { state: state + 1 };
                        GeneratorState::Yielded(state + 1)
                    }
                }
                GeneratorA::Exit => {
                    panic!("already exited")
                }
            }
        }
    }

    let mut g1 = Box::pin(GeneratorA::new());
    println!("yield {:?}", g1.as_mut().resume(3));
    println!("yield {:?}", g1.as_mut().resume(3));
    println!("yield {:?}", g1.as_mut().resume(3));
    println!("yield {:?}", g1.as_mut().resume(3));
    println!("yield {:?}", g1.as_mut().resume(3));
    println!("yield {:?}", g1.as_mut().resume(3));

    let mut g2 = Box::pin(GeneratorA::new());
    while let GeneratorState::Yielded(num) = g2.as_mut().resume(7) {
        println!("num: {num}");
    }
}

实现 #

查看 HIR 代码, cargo rustc -- -Z unpretty=hir 或者在 playground 中查看

HIR 可以将其理解为 AST 的另一中表示方式,主要是将语法糖做了转换 MIR HIR 被转换成了更低级的结构,生命周期在这里会被抹除

fn main() {}

#[inline(never)]
async fn foo() -> usize { 5 }

async fn bar() -> usize { foo().await }

ResumeTy 来自 https://github.com/rust-lang/rust/blob/master/library/core/src/future/mod.rs (TODO)