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);
}
}
主要特性
- 支持恢复参数和完成值
- 无内存分配
- 无运行时依赖
- 如果设置
default-features = []
则连编译时依赖也没有
- 如果设置
- 基于标准语言特性构建,没有平台特定的技巧
安装
在项目目录中运行以下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宏在编译时生成高效的状态机代码,相比运行时解决方案有更好的性能表现。生成的代码避免了堆分配,除非你的生成器本身需要捕获外部变量。
限制
- 生成器一旦开始执行,就不能被重置或重复使用
- 目前不支持自引用生成器
- 错误处理需要显式管理
总结
genawaiter-proc-macro
为Rust提供了一种简洁的方式来创建异步生成器,特别适合需要逐步产生异步数据的场景。它的proc-macro方法提供了编译时安全保证和良好的性能特性,同时保持了代码的可读性。