#递归 # #转换 #尾调用

tco

将尾调用递归函数转换为非递归的宏

2个版本

0.0.2 2020年5月1日
0.0.1 2020年5月1日

#640 in 进程宏

MIT/Apache

8KB
105 代码行

TCO

TCO是一个尾调用优化库。它是一个可以附加到项函数上的概念属性宏,如果它们处于尾调用格式,则可以对其进行优化。

限制

它不是很智能。

它实际上并没有验证函数是否为尾调用函数。

它只适用于自由函数(例如,不在impl块中的fn foo(bar: Bar) -> u32)。

它可能存在传递非复制参数的问题。

它未使用引用进行测试。

它不支持相互递归。

它只支持函数参数中的基本模式(不支持元组解构)。

它不能将非尾调用函数转换为尾调用函数。

需要帮助

这只是一个概念证明。我很乐意帮助完善它。

替代方案

示例

说得够多了,让我们看看示例!

同步示例:阶乘

#[tco::rewrite]
fn fac_with_acc(n: u128, acc: u128) -> u128 {
    if n > 1 {
        fac_with_acc(n - 1, acc * n)
    } else {
        acc
    }
}

展开为

fn fac_with_acc(n: u128, acc: u128) -> u128 {
    let mut n = n;
    let mut acc = acc;
    '__tco_loop: loop {
        return {
            if n > 1 {
                {
                    let __tco_0 = (n - 1, acc * n);
                    n = __tco_0.0;
                    acc = __tco_0.1;
                    continue '__tco_loop;
                }
            } else {
                acc
            }
        };
    }
}

异步示例:阶乘

#[tco::rewrite]
async fn fac_with_acc(n: u128, acc: u128) -> u128 {
    if n > 1 {
        fac_with_acc(n - 1, acc * n).await
    } else {
        acc
    }
}

展开为

async fn fac_with_acc(n: u128, acc: u128) -> u128 {
    let mut n = n;
    let mut acc = acc;
    '__tco_loop: loop {
        return {
            if n > 1 {
                {
                    let __tco_0 = (n - 1, acc * n);
                    n = __tco_0.0;
                    acc = __tco_0.1;
                    continue '__tco_loop;
                }
            } else {
                acc
            }
        };
    }
}

如果没有tco::rewrite属性,您将得到以下错误

error[E0733]: recursion in an `async fn` requires boxing
 --> $DIR/await_no_tco.rs:6:46
  |
6 | async fn fac_with_acc(n: u128, acc: u128) -> u128 {
  |                                              ^^^^ recursive `async fn`
  |
  = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`

依赖

~1.5MB
~36K SLoC