Рекурсивный макрос делает бесконечную рекурсию - PullRequest
0 голосов
/ 26 октября 2018

Я сделал простой макрос, который возвращает принятый параметр.

macro_rules! n {
    ($n:expr) => {{
        let val: usize = $n;
        match val {
            0 => 0,
            _ => n!(val - 1),
        }
    }};
}

Когда я компилирую этот код с параметром external-macro-backtrace, возникает ошибка:

error: recursion limit reached while expanding the macro `n`
  --> src/main.rs:15:18
   |
10 |   macro_rules! n {
   |  _-
   | |_|
   | |
11 | |     ($n:expr) => {{
12 | |         let val: usize = $n;
13 | |         match val {
14 | |             0 => 0,
15 | |             _ => n!(val - 1),
   | |                  ^^^^^^^^^^^
   | |                  |
   | |                  in this macro invocation
16 | |         }
17 | |     }};
18 | | }
   | | -
   | |_|
   | |_in this expansion of `n!`
   |   in this expansion of `n!`
...
31 | |     n!(1);
   | |     ------ in this macro invocation
   |
   = help: consider adding a `#![recursion_limit="128"]` attribute to your crate

Я изменил recursion_limit на 128 и выше, но сообщение об ошибке компилятора также увеличивается. Даже когда я звоню n!(0), он делает ту же ошибку. Я думаю, что это бесконечная рекурсия, но я не могу найти причину.

1 Ответ

0 голосов
/ 26 октября 2018

Ну, это действительно бесконечная рекурсия.Проверьте, в какой ваш макрос-вызов n!(0) будет расширен:

{
    let val: usize = 0;
    match val {
        0 => 0,
        _ => n!(0 - 1),
    }
}

... и, поскольку аргумент n! не может остановить рост, он будет повторяться (с n!(0 - 1 - 1)во втором ответвлении, затем n!(0 - 1 - 1 - 1) и т. д.) бесконечно.

Ключевым моментом здесь является то, что расширение макроса происходит во время компиляции, тогда как оператор match вы пытаетесь использовать для ограничениярекурсия вызывается только во время выполнения и не может остановить появление чего-либо до этого.К сожалению, не существует простого способа сделать это, так как Rust не будет оценивать аргументы макроса (даже если это константное выражение), и поэтому просто добавление ветки (0) => {0} в макрос не будет работать, так как макрос будет вызванкак (например) n!(1 - 1).

...