Function e_generator

Source
pub fn e_generator()
Expand description

§异步实现细节:生成器 与 协程

§历史

处理异步事件的三种方式:

  • Callback
  • Promise/Future
  • async/await

async/await 是目前体验最好的方式,Rust 要支持它并不容易。

§async/await 语法介绍

参考:Asynchronous Programming in Rust

async 两种用法:async fn 函数 和 async {} 块。

// async 函数,真正会返回 `Future<Output = u8>`,而不是表面看上去的 `u8`
async fn foo() -> u8 { 5 }

// async 块用法,返回 `impl Future<Output = u8>`
fn bar() -> impl Future<Output = u8> {
    // 这里 `async` 块返回  `impl Future<Output = u8>`
    async {
        let x: u8 = foo().await;
        x + 5
    }
}

await 将暂停当前函数的执行,直到执行者将 Future 结束为止。这为其他 Future 任务提供了计算的机会。

§生成器

Future 底层实现依赖于 生成器。 async/await 对应底层生成器 resume/yield

#![feature(generators, generator_trait)]
use std::ops::Generator;
use std::pin::Pin;

fn main() {
    let mut gen = || {
        yield 1;
        yield 2;
        yield 3;
        return 4;
    };
    // for _ in 0..4 {
    //     // 为了给嵌入式支持异步,多传入了一个空的unit给resume方法
    //     let c = Pin::new(&mut gen).resume(());
    //     println!("{:?}", c);
    // }


    let c = Pin::new(&mut gen).resume(());
    println!("{:?}", c);
    let c = Pin::new(&mut gen).resume(());
    println!("{:?}", c);
    let c = Pin::new(&mut gen).resume(());
    println!("{:?}", c);
    let c = Pin::new(&mut gen).resume(());
    println!("{:?}", c);
}

生成等价代码:

#![allow(unused)]
#![feature(generators, generator_trait)]
use std::ops::{Generator, GeneratorState};
use std::pin::Pin;

enum __Gen {
    // (0) 初始状态
    Start,
    // (1) resume方法执行以后
    State1(State1),
    // (2) resume方法执行以后
    State2(State2),
    // (3) resume方法执行以后
    State3(State3),
    // (4) resume方法执行以后,正好完成
    Done
}


struct State1 { x: u64 }
struct State2 { x: u64 }
struct State3 { x: u64 }

impl Generator for __Gen {
    type Yield = u64;
    type Return = u64;

    fn resume(self: Pin<&mut Self>, _: ()) -> GeneratorState<u64, u64> {
        let mut_ref = self.get_mut();
        match std::mem::replace(mut_ref, __Gen::Done) {
            __Gen::Start => {
                *mut_ref = __Gen::State1(State1{x: 1});
                GeneratorState::Yielded(1)
            }
            __Gen::State1(State1{x: 1}) => {
                *mut_ref  = __Gen::State2(State2{x: 2});
                GeneratorState::Yielded(2)
        }
            __Gen::State2(State2{x: 2}) => {
                *mut_ref = __Gen::State3(State3{x: 3});
                GeneratorState::Yielded(3)
            }
            __Gen::State3(State3{x: 3}) => {
                *mut_ref  = __Gen::Done;
                GeneratorState::Complete(4)
            }
            _ => {
                panic!("generator resumed after completion")
            }
        }
    }
}

fn main(){
    let mut gen = {
        __Gen::Start
    };

    for _ in 0..4 {
        println!("{:?}", unsafe{ Pin::new(&mut gen).resume(())});
    }
}

生成器基本用法:


#![allow(unused)]
#![feature(generators, generator_trait)]
use std::pin::Pin;

use std::ops::Generator;

pub fn up_to(limit: u64) -> impl Generator<Yield = u64, Return = u64> {
    move || {
        for x in 0..limit {
            yield x;
        }
        return limit;
    }
}
fn main(){
    let a = 10;
    let mut b = up_to(a);
    unsafe {
        for _ in 0..=10{
            let c = Pin::new(&mut b).resume(());
            println!("{:?}", c);
        }
    }
}

生成器变身为迭代器:

#![allow(unused)]
#![feature(generators, generator_trait)]
use std::pin::Pin;

use std::ops::{Generator, GeneratorState};

pub fn up_to() -> impl Generator<Yield = u64, Return = ()> {
    move || {
        let mut x = 0;
        loop {
            x += 1;
            yield x;
        }
        return ();
    }
}
fn main(){
    let mut gen = up_to();
    unsafe {
    for _ in 0..10{
        match Pin::new(&mut gen).resume(()) {
            GeneratorState::Yielded(i) => println!("{:?}", i),
            _ => println!("Completed"),
        }
    }
    }
}

生成器变身为 Future:


#![allow(unused)]
#![feature(generators, generator_trait)]

use std::ops::{Generator, GeneratorState};
use std::pin::Pin;

pub fn up_to(limit: u64) -> impl Generator<Yield = (), Return = Result<u64, ()>> {
    move || {
        for x in 0..limit {
            yield ();
        }
        return Ok(limit);
    }
}
fn main(){
    let limit = 2;
    let mut gen = up_to(limit);
    unsafe {
    for i in 0..=limit{
        match Pin::new(&mut gen).resume(()) {
            GeneratorState::Yielded(v) => println!("resume {:?} : Pending", i),
            GeneratorState::Complete(v) => println!("resume {:?} : Ready", i),
        }
    }
    }
}

跨 yield 借用会报错:

#![allow(unused)]
#![feature(generators, generator_trait)]

use std::ops::Generator;
use std::pin::Pin;

pub fn up_to(limit: u64) -> impl Generator<Yield = u64, Return = u64> {
    move || {
        let a = 5;
        let ref_a = &a;
        for x in 0..limit {
            yield x;
            if x == 5{
                yield *ref_a;
            }
        }
        return limit;
    }
}
fn main(){
    let a = 10;
    let mut b = up_to(a);
    unsafe {
        for _ in 0..=10{
            let c = Pin::new(&mut b).resume(());
            println!("{:?}", c);
        }
    }
}

自引用结构:

#![feature(generators, generator_trait)]
use std::ops::Generator;
use std::pin::Pin;
fn main(){
    let mut generator = move || {
        let to_borrow = String::from("Hello");
        let borrowed = &to_borrow;
        // error[E0626]: borrow may still be in use when generator yields
        yield borrowed.len();
        println!("{} world!", borrowed);
    };
}

模拟底层实现 generator :

#![allow(unused)]
#![feature(never_type)] // Force nightly compiler to be used in playground
// by betting on it's true that this type is named after it's stabilization date...
pub fn main() {
    let mut gen = GeneratorA::start();
    let mut gen2 = GeneratorA::start();

    if let GeneratorState::Yielded(n) = gen.resume() {
        println!("Got value {}", n);
    }

    // std::mem::swap(&mut gen, &mut gen2); // <--- Big problem!

    if let GeneratorState::Yielded(n) = gen2.resume() {
        println!("Got value {}", n);
    }

    // This would now start gen2 since we swapped them.
    if let GeneratorState::Complete(()) = gen.resume() {
        ()
    };

    if let GeneratorState::Complete(()) = gen2.resume() {
        ()
    };
}
enum GeneratorState<Y, R> {
    Yielded(Y),
    Complete(R),
}

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

enum GeneratorA {
    Enter,
    Yield1 {
        to_borrow: String,
        borrowed: *const String,
    },
    Exit,
}

impl GeneratorA {
    fn start() -> Self {
        GeneratorA::Enter
    }
}
impl Generator for GeneratorA {
    type Yield = usize;
    type Return = ();
    fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
            match self {
            GeneratorA::Enter => {
                let to_borrow = String::from("Hello");
                let borrowed = &to_borrow;
                let res = borrowed.len();
                *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};

                // We set the self-reference here
                if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
                    *borrowed = to_borrow;
                }

                GeneratorState::Yielded(res)
            }

            GeneratorA::Yield1 {borrowed, ..} => {
                let borrowed: &String = unsafe {&**borrowed};
                println!("{} world", borrowed);
                *self = GeneratorA::Exit;
                GeneratorState::Complete(())
            }
            GeneratorA::Exit => panic!("Can't advance an exited generator!"),
        }
    }
}

上面代码在 Safe Rust 下是存在问题的,有 UB 风险。

用 Pin 修正:

#![feature(generators, generator_trait)]
use std::ops::{Generator, GeneratorState};


pub fn main() {
    // 使用 static 关键字创建 immovable 生成器
    let gen1 = static || {
        let to_borrow = String::from("Hello");
        let borrowed = &to_borrow;
        yield borrowed.len();
        println!("{} world!", borrowed);
    };

    let gen2 = static || {
        let to_borrow = String::from("Hello");
        let borrowed = &to_borrow;
        yield borrowed.len();
        println!("{} world!", borrowed);
    };

    let mut pinned1 = Box::pin(gen1);
    let mut pinned2 = Box::pin(gen2);

    if let GeneratorState::Yielded(n) = pinned1.as_mut().resume(()) {
        println!("Gen1 got value {}", n);
    }

    if let GeneratorState::Yielded(n) = pinned2.as_mut().resume(()) {
        println!("Gen2 got value {}", n);
    };

    let _ = pinned1.as_mut().resume(());
    let _ = pinned2.as_mut().resume(());
}

第三方库 genawaiter:stable rust 实现 generator 。

https://github.com/whatisaphone/genawaiter