bigywater.tech
Rust · 83

dbg! 宏的实现方式

dbg! 比 println! 调试好用在哪?它到底输出到哪、会不会拿走所有权、release 里还在不在、背后怎么实现。这篇用真实运行和编译器展开把它的每个行为都验证了一遍。

stderr
dbg! 的输出位置(println! 在 stdout)
E0382
dbg!(v) 之后再使用 v
1 次
展开后表达式被求值的次数

输出格式

实测 · 本机执行输出
  源码:
    fn main() {
        let a = 2;
        let b = dbg!(a * 3) + 1;
        println!("b = {}", b);
        let v = vec![1, 2, 3];
        dbg!(&v);
        dbg!();
        let (x, y) = dbg!(10, "s");
        println!("x = {} y = {}", x, y);
    }
  编译:成功
  stdout: 'b = 7\nx = 10 y = s'
  stderr:
    [<实验目录>/src/u83a.rs:3:13] a * 3 = 6
    [<实验目录>/src/u83a.rs:6:5] &v = [
        1,
        2,
        3,
    ]
    [<实验目录>/src/u83a.rs:7:5]
    [<实验目录>/src/u83a.rs:8:18] 10 = 10
    [<实验目录>/src/u83a.rs:8:18] "s" = "s"

所有权

实测 · 本机执行输出
  源码:
      1 | fn main() {
      2 |     let v = vec![1, 2, 3];
      3 |     dbg!(v);
      4 |     println!("{:?}", v);
      5 | }
  编译:失败
    error[E0382]: borrow of moved value: `v`
     --> <实验目录>/src/u83b.rs:4:22
      |
    2 |     let v = vec![1, 2, 3];
      |         - move occurs because `v` has type `Vec<i32>`, which does not implement the `Copy` trait
    3 |     dbg!(v);
      |     ------- value moved here
    4 |     println!("{:?}", v);
      |                      ^ value borrowed here after move
      |
    help: consider borrowing instead of transferring ownership
      |
    3 |     dbg!(&v);
      |          +
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0382`.

release 里仍然输出

实测 · 本机执行输出
  编译:成功
  debug  : stdout='2' stderr 行数=1  [<实验目录>/src/u83c.rs:1:21] 1 + 1 = 2
  编译:成功
  release: stdout='2' stderr 行数=1  [<实验目录>/src/u83c.rs:1:21] 1 + 1 = 2

展开成什么

实测 · 本机执行输出
  pub fn f(a: i32) -> i32 {
      match a + 1 {
          tmp => {
              {
                  ::std::io::_eprint(format_args!("[{0}:{1}:{2}] {3} = {4:#?}\n",
                          "<实验目录>/src/u83d.rs",
                          1u32, 27u32, "a + 1", &&tmp as &dyn ::std::fmt::Debug));
              };
              tmp
          }
      }
  }
  编译:成功
  运行(退出码 0):
    表达式被求值 1 次,值 = 1
    [<实验目录>/src/u83e.rs:4:13] { n.set(n.get() + 1); n.get() } = 1

展开使用 RUSTC_BOOTSTRAP=1 -Z unpretty=expanded。Display 与 Debug 的区别见Display 与 Debug。

美化输出

实测 · 本机执行输出
  编译:成功;警告:
    warning: fields `name`, `tags`, and `pos` are never read
     --> <实验目录>/src/u83f.rs:1:29
      |
    1 | #[derive(Debug)] struct P { name: String, tags: Vec<u8>, pos: (i32, i32) }
      |                         -   ^^^^          ^^^^           ^^^
      |                         |
      |                         fields in this struct
      |
      = note: `P` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
      = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
    
    warning: 1 warning emitted
  stdout: 'P { name: "a", tags: [1, 2], pos: (3, 4) }'
  stderr:
    [<实验目录>/src/u83f.rs:2:76] &p = P {
        name: "a",
        tags: [
            1,
            2,
        ],
        pos: (
            3,
            4,
        ),
    }

分步演示

dbg!
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。展开用 RUSTC_BOOTSTRAP=1 -Z unpretty=expanded
    • 没有验证:dbg! 在多线程里输出交错的情况、stderr 重定向到文件时的格式、dbg! 对不实现 Debug 的类型的报错(未测)、macro-backtrace
    京ICP备2026067502号-1京公网安备11010502063487号