递归

宏可以调用自身,就像函数递归一样:

macro_rules! sum {
    ($base:expr) => { $base };
    ($a:expr, $($rest:expr),+) => { $a + sum!($($rest),+) };
}

让我们来看看 sum!(1, 2, 3) 的扩展:

   sum!(1, 2, 3)
//      ^  ^~~~
//     $a  $rest
=> 1 + sum!(2, 3)
//          ^  ^
//         $a  $rest
=> 1 + (2 + sum!(3))
//               ^
//               $base
=> 1 + (2 + (3))

递归限制

当编译器过于扩展宏时,它会放弃。默认情况下,编译器在将宏扩展到 64 级深度后会失败,因此例如以下扩展将导致失败:

sum!(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
     21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
     41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62)

// error: recursion limit reached while expanding the macro `sum`
//  --> <anon>:3:46
// 3 |>         ($a:expr, $($rest:expr),+) => { $a + sum!($($rest),+) };
//   |>                                              ^^^^^^^^^^^^^^^^

当达到递归限制时,你应该考虑重构你的宏,例如

  • 也许递归可以被重复取代?
  • 也许输入格式可以改为不那么花哨的东西,所以我们不需要递归来匹配它?

如果有任何合理的原因,64 级是不够的,你总是可以增加使用属性调用宏的包的限制:

#![recursion_limit="128"]
//                  ^~~ set the recursion limit to 128 levels deep.