println! error: expected a literal / format argument must be a string literal

println! error: expected a literal / format argument must be a string literal

这个极其简单的 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");    
}

工作正常。

我不清楚这个错误的含义,Google 搜索也没有真正阐明它。为什么将 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 必须包含标识符的来源和足够的信息以确定它是否可以用作格式字符串。此外,它可能与类型推断的交互效果不佳 - 您可能希望在选择类型之前就知道类型!

错误消息要求输入“字符串文字”。 What does the word "literal" mean? asks about what that means, which links to the Wikipedia entry:

a literal is a notation for representing a fixed value in source code

"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");

产出

Some stuff that will always repeat, something variable: I am the variable data