调试

rustc提供了一些工具用来调试宏。其中,最有用的之一是trace_macros!。它会指示编译器,在每一个宏调用被展开之前将其转印出来。例如,给定下列代码:

  1. # // Note: make sure to use a nightly channel compiler.
  2. #![feature(trace_macros)]
  3. macro_rules! each_tt {
  4. () => {};
  5. ($_tt:tt $($rest:tt)*) => {each_tt!($($rest)*);};
  6. }
  7. each_tt!(foo bar baz quux);
  8. trace_macros!(true);
  9. each_tt!(spim wak plee whum);
  10. trace_macros!(false);
  11. each_tt!(trom qlip winp xod);
  12. #
  13. # fn main() {}

编译输出将包含:

  1. each_tt! { spim wak plee whum }
  2. each_tt! { wak plee whum }
  3. each_tt! { plee whum }
  4. each_tt! { whum }
  5. each_tt! { }

它在调试递归很深的宏时尤其有用。同时,它可以在命令提示符中被打开,在编译指令中附加-Z trace-macros即可。

另一有用的宏是log_syntax!。它将使得编译器输出所有经过编译器处理的标记。举个例子,下述代码可以让编译器唱一首歌:

  1. # // Note: make sure to use a nightly channel compiler.
  2. #![feature(log_syntax)]
  3. macro_rules! sing {
  4. () => {};
  5. ($tt:tt $($rest:tt)*) => {log_syntax!($tt); sing!($($rest)*);};
  6. }
  7. sing! {
  8. ^ < @ < . @ *
  9. '\x08' '{' '"' _ # ' '
  10. - @ '$' && / _ %
  11. ! ( '\t' @ | = >
  12. ; '\x08' '\'' + '$' ? '\x7f'
  13. , # '"' ~ | ) '\x07'
  14. }
  15. #
  16. # fn main() {}

比起trace_macros!来说,它能够做一些更有针对性的调试。

有时问题出在宏展开后的结果里。对于这种情况,可用编译命令--pretty来勘察。给出下列代码:

  1. // Shorthand for initialising a `String`.
  2. macro_rules! S {
  3. ($e:expr) => {String::from($e)};
  4. }
  5. fn main() {
  6. let world = S!("World");
  7. println!("Hello, {}!", world);
  8. }

并用如下编译命令进行编译,

  1. rustc -Z unstable-options --pretty expanded hello.rs

将输出如下内容(略经修改以符合排版):

  1. #![feature(no_std, prelude_import)]
  2. #![no_std]
  3. #[prelude_import]
  4. use std::prelude::v1::*;
  5. #[macro_use]
  6. extern crate std as std;
  7. // Shorthand for initialising a `String`.
  8. fn main() {
  9. let world = String::from("World");
  10. ::std::io::_print(::std::fmt::Arguments::new_v1(
  11. {
  12. static __STATIC_FMTSTR: &'static [&'static str]
  13. = &["Hello, ", "!\n"];
  14. __STATIC_FMTSTR
  15. },
  16. &match (&world,) {
  17. (__arg0,) => [
  18. ::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Display::fmt)
  19. ],
  20. }
  21. ));
  22. }

--pretty还有其它一些可用选项,可通过rustc -Z unstable-options --help -v来列出。此处并不提供该选项表;因为,正如指令本身所暗示的,表中的一切内容在任何时间点都有可能发生改变。