宏
print!
、
println!
、
eprint!
、
eprintln!
、
write!
、
writeln!
和
format!
是一种特殊情况,它们隐式地接受要格式化的任何参数的引用。
这些宏不像普通函数和宏那样按照惯例行事,它们悄无声息地接受引用,这是它们与普通函数和宏的区别之一。
fn main() {
let x = 5;
println!("{}", x);
}
通过夜间编译器运行
rustc -Z unstable-options --pretty expanded
,我们可以看到
println!
展开的结果:
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
fn main() {
let x = 5;
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["", "\n"],
&match (&x,) {
(arg0,) => [::core::fmt::ArgumentV1::new(
arg0,
::core::fmt::Display::fmt,
)],
},
));
};
}
进一步整理,就是这样:
use std::{fmt, io};
fn main() {
let x = 5;
io::_print(fmt::Arguments::new_v1(
&["", "\n"],
&[fmt::ArgumentV1::new(&x, fmt::Display::fmt)],
));
}
注意
&x
。
如果你写
println!("{}", &x)
,那么你就处理了两层引用;这是因为有一个实现了
std::fmt::Display
的
&T
,其中
T
实现了
Display
(显示为
impl<'a, T> Display for &'a T where T: Display + ?Sized
),它只是将其传递了下去。你也可以写成
&&&&&&&&&&&&&&&&&&&&&&&x
。
2023年初更新:
自2021年中以来,所需的调用方式为rustc -Zunpretty=expanded
,而不是rustc -Zunstable-options --pretty=expanded
。
自2023-01-28左右(https://github.com/rust-lang/rust/pull/106745),format_args!
是AST的一部分,因此println!("{}", x)
的展开形式为::std::io::_print(format_args!("{0}\n", x));
,不再暴露Arguments::new_v1
构造和&x
方面。这对于各种原因都是好的(请阅读#106745的描述),但破坏了我在这里清晰演示x
只被引用的情况。 (这就是为什么我将其作为附注添加到末尾而不是更新答案的原因-因为它不再起作用。)
&x
目前编译出的代码比x
慢了约 6%。https://stackoverflow.com/questions/76361472/why-formatting-value-vs-ref-generates-different-assembly-in-rust - Yuri Astrakhan