86

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Mike Vella
  • 10,187
  • 14
  • 59
  • 86

4 Answers4

71

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!.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
evotopid
  • 5,288
  • 2
  • 26
  • 41
  • 5
    That does work, although it's a bit ugly! Why does the original error happen? Printing a string in this way is a very common task in almost all modern languages and the error message given is really ambiguous. – Mike Vella Jan 01 '15 at 21:19
  • 3
    It's not ambigous, but it does make the assumption you know what a "literal" is. `"foo"` is a string literal. `8` is a numeric literal. `let s = "foo"` is assigning the value of a string literal to a variable, and `println!(s)` is passing a variable to the macro. – Shepmaster Jan 01 '15 at 21:24
  • 3
    @Shepmaster thanks - could you provide an answer providing some more detail on this? I expect many other rust users will encounter this error coming from languages like Python and it would be helpful to have your explanation. – Mike Vella Jan 01 '15 at 21:26
  • what macro check is the literal (string, numeric and so on) and a variable assigned with let is not any of those things. – paul Apr 05 '18 at 21:15
  • 2
    It's not expected that coming from a new language you should just guess how functions work. I mean, compare C++'s `std::cout`, which has a vastly different syntax. Learning a new language means read tutorials and documentation, and only use what you've learned. Otherwise you might get unexpected behavior or even worse, security vulnerabilities (Or just, weird bugs like using a mutable array function's return value, without realizing that it changed the original array. Rust's `mut` fixes that but other languages don't have `mut`). – Nicholas Pipitone Oct 09 '18 at 21:08
70

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    Why is it impossible at compile time to tell that c came from a literal at compile time? – Mike Vella Jan 01 '15 at 21:32
  • I think the real issue is that if `c = "{}"` and the macro is resolved at compile time, it would be really hard for the compiler to be sure if it further arguments are to be expected and so the validation wouldn't be possible to be assured at compile time... – evotopid Jan 01 '15 at 21:33
  • 4
    I assume it's not impossible *forever*, just not right now with how macros are implemented. Macros take [portions of the AST](http://doc.rust-lang.org/guide-macros.html) which I assume only has type information. Your question would involve knowing the source of the type and enough information to determine it's "safe". In addition, it might interact poorly with type inference - you want to know the type before it's been picked yet! – Shepmaster Jan 01 '15 at 21:35
  • Thanks, I think I understand now (or I'm less confused anyway) - @Shepmaster, could you add that bit about Macros and the AST to your answer? Quite useful info. – Mike Vella Jan 01 '15 at 21:42
  • I'd argue there's some gain to be had by making this work. log4rs uses the same macro scheme. Any time an error string gets kicked back in a result from a function and you want to log it with say `error!(err)`, you can't. You have to use the `error!("{}", err)` hack. It's not the end of the world, but it is a mild irritant. :) – marathon Aug 09 '17 at 03:38
9

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.

sol_var
  • 847
  • 1
  • 9
  • 9
  • 2
    If you're going to go with the macro approach, you may as well just put the `println!` inside the macro, like so `macro_rules! cell_format { ( $e:expr ) => ( println!("{:<10}", $e) ); }` – mattforni Mar 09 '16 at 16:57
5

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

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
tjb
  • 11,480
  • 9
  • 70
  • 91