「这是我参与11月更文挑战的第 21 天,活动详情查看:2021最后一次更文挑战」
现在,任何词都被相应的分支处理,我们只需要处理最后的情况,即栈包含一个项目,并且没有更多的词了。
macro_rules! rpn {
// ...
([ $result:expr ]) => {
$result
};
}
此时,如果你用一个空的栈和RPN表达式来调用这个宏,它已经会产生一个正确的结果。
println!("{}", rpn!([] 2 3 + 4 *)); // 20
然而,我们的栈是一个实现细节,我们真的不希望每个消费者都传入一个空的堆栈,所以让我们在最后再添加一个接续分支,作为一个入口点,并自动添加[]。
macro_rules! rpn {
// ...
($($tokens:tt)*) => {
rpn!([] $($tokens)*)
};
}
println!("{}", rpn!(2 3 + 4 *)); // 20
我们的宏甚至适用于更复杂的表达式。
println!("{}", rpn!(15 7 1 1 + - / 3 * 2 1 1 + + -)); // 5
那如果出错了怎么办?现在,对于正确的RPN表达式来说,一切似乎都工作得很顺利,但对于一个可以投入生产的宏来说,我们需要确保它也能处理无效的输入,并且有合理的错误信息。
首先,让我们试着在中间插入另一个数字,看看会发生什么:
println!("{}", rpn!(2 3 7 + 4 *));
输出:
error[E0277]: the trait bound `[{integer}; 2]: std::fmt::Display` is not satisfied
--> src/main.rs:36:20
|
36 | println!("{}", rpn!(2 3 7 + 4 *));
| ^^^^^^^^^^^^^^^^^ `[{integer}; 2]` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
|
= help: the trait `std::fmt::Display` is not implemented for `[{integer}; 2]`
= note: required by `std::fmt::Display::fmt`
好吧,这个输出看起来没有帮助,因为它没有提供任何与表达式中的实际错误相关的信息。
为了弄清发生了什么,我们需要对我们的宏进行调试。为此,我们将使用 trace_macros 功能(和其他可选的编译器功能一样,你需要一个Rust nightly版本)。我们不想跟踪 println! 调用,所以我们将把我们的RPN计算分离到一个变量中。