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

This extremely simple Rust program:

fn main() {
    let c = "hello";
    println!(c);
}

throws the following compile-time error:

error: expected a literal
 --> src/main.rs:3:14
  |
3 |     println!(c);
  |              ^

In previous versions of Rust, the error said:

error: format argument must be a string literal.
     println!(c);
              ^

Replacing the program with:

fn main() {
    println!("Hello");    
}

Works fine.

The meaning of this error isn't clear to me and a Google search hasn't really shed light on it. Why does passing c to the println! macro cause a compile time error? This seems like quite unusual behaviour.


Solution 1:

This should work:

fn main() {
    let c = "hello";
    println!("{}", c);
}

The string "{}" is a template where {} will be replaced by the next argument passed to println!.

Solution 2:

TL;DR If you don't care why and just want to fix it, see the sibling answer.


The reason that

fn main() {
    let c = "hello";
    println!(c);
}

Cannot work is because the println! macro looks at the string at compile time and verifies that the arguments and argument specifiers match in amount and type (this is a very good thing!). At this point in time, during macro evaluation, it's not possible to tell that c came from a literal or a function or what have you.

Here's an example of what the macro expands out to:

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)]
        ))
    }
};

I don't think that it's actually impossible for the compiler to figure this out, but it would probably take a lot of work with potentially little gain. Macros operate on portions of the AST and the AST only has type information. To work in this case, the AST would have to include the source of the identifier and enough information to determine it's acceptable to be used as a format string. In addition, it might interact poorly with type inference - you'd want to know the type before it's been picked yet!

The error message asks for a "string literal". 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" is a string literal, 8 is a numeric literal. let s = "foo" is a statement that assigns the value of a string literal to an identifier (variable). println!(s) is a statement that provides an identifier to the macro.

Solution 3:

If your format string will be reused only a moderate number of times, and only some variable data will be changed, then a small function may be a better option than a macro:

fn pr(x: &str) {
    println!("Some stuff that will always repeat, something variable: {}", x);
}

pr("I am the variable data");

Outputs

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

Solution 4:

If you really want to define the first argument of println! in one place, I found a way to do it. You can use a macro:

macro_rules! hello {() => ("hello")};
println!(hello!());

Doesn't look too useful here, but I wanted to use the same formatting in a few places, and in this case the method was very helpful:

macro_rules! cell_format {() => ("{:<10}")};  // Pads with spaces on right
                                              // to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);

The macro saved me from having to duplicate the formatting option in my code.

You could also, obviously, make the macro more fancy and take arguments if necessary to print different things with different arguments.