这个非常简单的 Rust 程序:
fn main() {
let c = "hello";
println!(c);
}
引发以下编译时错误:
error: expected a literal
--> src/main.rs:3:14
|
3 | println!(c);
| ^
在以前版本的 Rust 中,错误说:
error: format argument must be a string literal.
println!(c);
^
将程序替换为:
fn main() {
println!("Hello");
}
工作正常。
我不清楚这个错误的含义,谷歌搜索也没有真正阐明它。为什么将c
传递给println!
宏会导致编译时错误?这似乎是非常不寻常的行为。
这应该有效:
fn main() {
let c = "hello";
println!("{}", c);
}
字符串 "{}"
是一个模板,其中{}
将被传递给 println!
的下一个参数替换。
TL;DR 如果您不在乎原因,只想修复它,请参阅兄弟姐妹答案。
<小时 />原因
fn main() {
let c = "hello";
println!(c);
}
无法工作是因为println!
宏在编译时查看字符串并验证参数和参数说明符在数量和类型上是否匹配(这是一件非常好的事情!在这个时间点,在宏观评估期间,不可能分辨c
来自文字或函数或你有什么。
下面是宏扩展到的示例:
let c = "hello";
match (&c,) {
(__arg0,) => {
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR: &'static [&'static str] = &[""];
::std::io::stdio::println_args(&::std::fmt::Arguments::new(
__STATIC_FMTSTR,
&[::std::fmt::argument(::std::fmt::Show::fmt, __arg0)]
))
}
};
我不认为编译器实际上不可能弄清楚这一点,但它可能需要大量的工作,而可能几乎没有收获。宏在 AST 的某些部分上运行,并且 AST 仅具有类型信息。若要在这种情况下工作,AST 必须包含标识符的源和足够的信息,以确定是否可以将其用作格式字符串。此外,它可能与类型推断交互不良 - 在选择类型之前,您想知道类型!
错误消息要求输入"字符串文本"。"字面意思"是什么意思?询问这意味着什么,链接到维基百科条目:
文本是用于表示源代码中的固定值的表示法
"foo"
是字符串文本,8
是数字文本。 let s = "foo"
是将字符串文本的值分配给标识符(变量)的语句。 println!(s)
是为宏提供标识符的语句。
如果你真的想定义println的第一个参数! 在一个地方,我找到了一种方法。 您可以使用宏:
macro_rules! hello {() => ("hello")};
println!(hello!());
在这里看起来不太有用,但我想在一些地方使用相同的格式,在这种情况下,该方法非常有用:
macro_rules! cell_format {() => ("{:<10}")}; // Pads with spaces on right
// to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);
宏使我不必在代码中复制格式选项。
显然,您也可以使宏更加花哨,并在必要时进行论证,以使用不同的参数打印不同的东西。
如果您的格式字符串只被重用了中等次数,并且只会更改一些变量数据,那么小函数可能是比宏更好的选择:
fn pr(x: &str) {
println!("Some stuff that will always repeat, something variable: {}", x);
}
pr("I am the variable data");
输出
有些东西总是会重复,有些东西是可变的:我是可变数据