Rust 的 println! 是如何工作的?宏执行取消引用?

How Does Rust's println! macro perform dereferencing?

println! 宏可以处理值和引用,而无需显式解除引用。

首先,创建一个向量

let v = vec![0, 2, 3, -4];
  1. 正在打印来自 vec.iter

    的引用
    for x in v.iter() {
        println!("x: {}", x);
    }
    
  2. 打印来自 vec.iter

    的解除引用的元素
    for x in v.iter() {
        println!("x: {}", *x);
    }
    
  3. 正在打印来自 vec

    的值
    for x in v {
        println!("x: {}", x);
    }
    

案例1中的内部解引用是如何完成的?

我内部知道 println! 进行了另一个宏调用,但链中的最后一个宏 format_args! 是在编译器级别实现的,我没有看到它。

macro_rules! println {
    ($fmt:expr) => (print!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

macro_rules! print {
    ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
}

macro_rules! format_args {
    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
}

Source code Reference

这里重要的是,在格式字符串中使用 {} 会在传递的值上调用 Display 特征。

正如预期的那样,the i32 type 实现了 Display,这就是让您的案例 #2 和案例 #3 起作用的原因。因为他们得到的是标准 i32 值,而不是参考值,所以一切正常。

对于您的案例 #1,x 将是 &i32,这似乎是您问题的核心。答案在 the Display trait 中。显示包含以下内容:

impl<'a, T> Display for &'a T 
where
    T: Display + ?Sized

表示 "for the reference type of T, implement Display if T implements Display"。这意味着因为 i32 实现了 Display,引用类型也自动实现了它。

这里编译器没有进行特殊的类型处理。编译器实现的代码将该责任传递给 Display 特性的实现。