8 个版本
0.1.8 | 2022 年 4 月 1 日 |
---|---|
0.1.7 | 2022 年 3 月 24 日 |
0.1.6 | 2022 年 2 月 24 日 |
#850 in 异步
53KB
1K SLoC
gentian
此 crate 提供了一个证明概念的 proc macro 属性,允许将生成器转换为状态机。此 crate 将作为辅助工具用于 v2ray-rust。
动机
Rust 的 async
和 await
非常酷。但当手动实现 Future
/Stream
/Poll
时,问题出现了。要么放弃一定的性能来扩展 future
的生命周期或手动维护状态机。当 poll
的逻辑逐渐复杂时,状态机的正确性变得更加难以保证。不稳定的 Rust 标准库提供了 generator
,但它不适用于解决上述问题。总之,这个 crate 旨在支持在 Rust 控制流子集中使用 yield/yield return
。就像 async
和 await
一样,它将被编译成状态机。
示例
use gentian::gentian;
#[cfg(test)]
struct MyGenerator {
my_state_1: usize,
pub my_state_2: usize,
pub num: u32,
pub num1: u32,
}
#[cfg(test)]
impl MyGenerator {
pub fn new() -> MyGenerator {
MyGenerator {
my_state_1: 0,
my_state_2: 0,
num: 0,
num1: 0,
}
}
#[gentian]
#[gentian_attr(state=self.my_state_1)]
pub fn test_simple(&mut self) {
loop {
println!("Hello, ");
//co_yield;
while self.num1 < 99 {
println!("Generator{}", self.num1);
self.num1 += 1;
co_yield;
}
return;
}
}
// state_name , return_default_value
#[gentian]
#[gentian_attr(state=self.my_state_2,ret_val=0u32)]
pub fn get_odd(&mut self) -> u32 {
loop {
if self.num % 2 == 1 {
co_yield(self.num);
}
self.num += 1;
}
}
}
#[test]
fn test_generator_proc_macro() {
let mut gen = MyGenerator::new();
gen.test_simple(); // print Hello,
for _ in 0..200 {
gen.test_simple(); // only print 99 times `Generator`
}
gen.test_simple(); // print nothing
assert_eq!(gen.num1, 99);
for i in (1u32..1000).step_by(2) {
assert_eq!(gen.get_odd(), i);
}
}
说明
以下代码不是有效的 Rust 函数,但它展示了生成代码的逻辑。
pub fn poll_read_decrypted<R>(
&mut self,
ctx: &mut Context<'_>,
r: &mut R,
dst: &mut [u8],
) -> Poll<io::Result<(usize)>>
where
R: AsyncRead + Unpin,
{
co_yield;
co_return(Poll::Pending);
if cond1{
f();
co_return(Poll::Pending);
g();
}else{
let c=p();
co_return(Poll::Ready(Ok(c)));
q();
}
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
// This would break only the inner loop
//break;
// This breaks the outer loop
break 'outer;
}
println!("This point will never be reached");
}
loop{
if cond2{
return Poll::Ready();
} else{
break;
}
}
loop{
if cond3{
println!("cond3 is true");
continue;
} else{
break;
}
}
while not_done{
let c=do1();
co_return(Poll::Ready(Ok(c)));
do2();
if cond4{
break;
}
}
}
上述代码将被展开为
pub fn poll_read_decrypted<R>(
&mut self,
ctx: &mut Context<'_>,
r: &mut R,
dst: &mut [u8],
) -> Poll<io::Result<(usize)>>
where
R: AsyncRead + Unpin,
{
'genloop: loop {
match state {
50 => {
break 'genloop;
}
0 => {
state = 1;
return;
}
1 => {
state = 2;
return Poll::Pending;
}
2 => {
state = 18;
if cond1 {
state = 3;
continue 'genloop;
}
}
3 => {
f();
state = 4;
return Poll::Pending;
}
4 => {
g();
state = 5;
}
5 => {
println!("Entered the outer loop");
println!("Entered the inner loop");
state = 9;
if cond2 {
state = 6;
continue 'genloop;
}
}
6 => {
state = 7;
return Poll::Ready();
}
7 => {
state = 50;
}
9 => {
state = 12;
if cond3 {
state = 10;
continue 'genloop;
}
}
10 => {
println!("cond3 is true");
state = 9;
}
12 => {
state = 7;
if not_done {
state = 13;
continue 'genloop;
}
}
13 => {
let c = do1();
state = 14;
return Poll::Ready(Ok(c));
}
14 => {
do2();
state = 12;
if cond4 {
state = 7;
continue 'genloop;
}
}
18 => {
let c = p();
state = 19;
return Poll::Ready(Ok(c));
}
19 => {
q();
state = 5;
}
_ => {
break 'genloop;
}
}
}
return Poll::Pending;
}
上述代码的控制流图(CFG)为
依赖项
~2MB
~44K SLoC