Rust异步生成器库genawaiter-proc-macro的使用:基于proc-macro的轻量级async/await代码生成与状态机管理

Rust异步生成器库genawaiter-proc-macro的使用:基于proc-macro的轻量级async/await代码生成与状态机管理

内容中提供的示例代码:

let odd_numbers_less_than_ten = gen!({
    let mut n = 1;
    while n < 10 {
        yield_!(n); // 在任意点暂停函数并返回一个值
        n += 2;
    }
});

// 生成器可以作为普通迭代器使用
for num in odd_numbers_less_than_ten {
    println!("{}", num);
}

运行结果:

1
3
5
7
9

完整示例demo

以下是一个更完整的示例,展示如何使用genawaiter-proc-macro创建异步生成器:

use genawaiter::{gen, yield_};

#[tokio::main]
async fn main() {
    // 创建一个异步生成器
    let async_gen = gen!({
        yield_!(1).await;
        yield_!(2).await;
        yield_!(3).await;
    });

    // 使用生成器
    let mut stream = async_gen.into_stream();
    while let Some(value) = stream.next().await {
        println!("Got: {}", value);
    }
    
    // 更复杂的示例 - 生成斐波那契数列
    let fibonacci = gen!({
        let mut a = 0;
        let mut b = 1;
        
        yield_!(a).await; // 返回第一个数字0
        
        loop {
            yield_!(b).await; // 返回当前数字
            let next = a + b;
            a = b;
            b = next;
            
            if b > 1000 { // 限制在1000以内
                break;
            }
        }
    });
    
    println!("Fibonacci sequence:");
    let mut fib_stream = fibonacci.into_stream();
    while let Some(num) = fib_stream.next().await {
        print!("{} ", num);
    }
}

主要特性

  1. 支持恢复参数和完成值
  2. 无内存分配
  3. 无运行时依赖
    • 如果设置 default-features = [] 则连编译时依赖也没有
  4. 基于标准语言特性构建,没有平台特定的技巧

安装

在项目目录中运行以下Cargo命令:

cargo add genawaiter-proc-macro

或者在Cargo.toml中添加:

genawaiter-proc-macro = "0.99.1"

开发

安装先决条件

  • Rust
  • pre-commit

安装pre-commit钩子

pre-commit install

这会安装一个Git钩子,在每次提交前运行快速检查。

运行测试

cargo test

许可证

MIT OR Apache-2.0


1 回复

Rust异步生成器库genawaiter-proc-macro使用指南

介绍

genawaiter-proc-macro是一个基于proc-macro的轻量级Rust库,用于简化异步生成器的创建和管理。它提供了一种声明式的方式来编写异步生成器,自动处理状态机管理,让开发者可以专注于业务逻辑而不是底层状态转换。

该库特别适合需要生成异步数据流的场景,如:

  • 实现自定义异步迭代器
  • 构建复杂异步操作管道
  • 处理分批次数据加载
  • 创建响应式数据源

基本使用方法

首先在Cargo.toml中添加依赖:

[dependencies]
genawaiter = { version = "0.4", features = ["proc_macro"] }

基本示例

use genawaiter::{sync::gen, yield_};

#[tokio::main]
async fn main() {
    // 创建一个简单的异步生成器
    let generator = gen! {
        yield_!(10).await;
        yield_!(20).await;
        yield_!(30).await;
    };

    // 消费生成器
    let mut stream = generator.into_stream();
    while let Some(value) = stream.next().await {
        println!("Received: {}", value);
    }
}

带状态的生成器

use genawaiter::{sync::gen, yield_};

#[tokio::main]
async fn main() {
    let count_to = 5;
    
    let generator = gen! {
        for i in 0..count_to {
            yield_!(i * 2).await;
        }
    };

    let mut stream = generator.into_stream();
    while let Some(value) = stream.next().await {
        println!("Value: {}", value);
    }
}

高级特性

异步操作集成

use genawaiter::{sync::gen, yield_};
use std::time::Duration;
use tokio::time::sleep;

#[tokio::main]
async fn main() {
    let generator = gen! {
        for i in 0..3 {
            sleep(Duration::from_secs(1)).await;
            yield_!(format!("Item {} after 1s", i)).await;
        }
    };

    let mut stream = generator.into_stream();
    while let Some(msg) = stream.next().await {
        println!("{}", msg);
    }
}

错误处理

use genawaiter::{sync::gen, yield_};
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let generator = gen! {
        yield_!(Ok("First")).await;
        yield_!(Err("Something went wrong"))?;
        yield_!(Ok("This won't be reached")).await;
    };

    let mut stream = generator.into_stream();
    while let Some(result) = stream.next().await {
        println!("{:?}", result);
    }
    
    Ok(())
}

与其他异步库集成

use genawaiter::{sync::gen, yield_};
use futures::StreamExt;

#[tokio::main]
async fn main() {
    let generator = gen! {
        for i in 0..5 {
            if i % 2 == 0 {
                yield_!(i).await;
            }
        }
    };

    // 使用futures的StreamExt方法
    let sum: i32 = generator
        .into_stream()
        .filter(|x| futures::future::ready(*x > 1))
        .fold(0, |acc, x| async move { acc + x })
        .await;
        
    println!("Sum: {}", sum);
}

完整示例代码

下面是一个结合了多个特性的完整示例:

use genawaiter::{sync::gen, yield_};
use std::{error::Error, time::Duration};
use tokio::time::sleep;
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // 创建一个复杂生成器,结合多种特性
    let generator = gen! {
        // 初始值
        yield_!("Starting data stream...").await;
        
        // 异步操作
        sleep(Duration::from_millis(500)).await;
        yield_!("First batch ready").await;
        
        // 带状态的生成
        for i in 1..=3 {
            sleep(Duration::from_millis(200)).await;
            yield_!(format!("Processing item {}", i)).await;
        }
        
        // 错误处理
        yield_!(Ok("Valid data point")).await;
        
        // 条件生成
        for i in 4..6 {
            if i % 2 == 0 {
                yield_!(i).await;
            } else {
                yield_!(Err(format!("Error on odd number {}", i)))?;
            }
        }
        
        yield_!("Stream completed").await;
    };

    // 使用StreamExt处理流
    let results: Vec<_> = generator
        .into_stream()
        .filter_map(|item| async move {
            match item {
                Ok(v) => Some(v),
                Err(e) => {
                    println!("Error encountered: {}", e);
                    None
                }
            }
        })
        .collect()
        .await;
    
    println!("Final results: {:?}", results);
    Ok(())
}

性能考虑

genawaiter-proc-macro通过proc宏在编译时生成高效的状态机代码,相比运行时解决方案有更好的性能表现。生成的代码避免了堆分配,除非你的生成器本身需要捕获外部变量。

限制

  1. 生成器一旦开始执行,就不能被重置或重复使用
  2. 目前不支持自引用生成器
  3. 错误处理需要显式管理

总结

genawaiter-proc-macro为Rust提供了一种简洁的方式来创建异步生成器,特别适合需要逐步产生异步数据的场景。它的proc-macro方法提供了编译时安全保证和良好的性能特性,同时保持了代码的可读性。

回到顶部