Rust: print! and println! (and other similar macro pairs) accept differing types of "format strings"

Created on 15 Jan 2018  路  17Comments  路  Source: rust-lang/rust

println! accepts non-string literals as the format string:

fn main() {
    println!(12.3);
}

print! does not:

fn main() {
    print!(12.3);
}
error: format argument must be a string literal.
 --> src/main.rs:2:12
  |
2 |     print!(12.3);
  |            ^^^^

This is because println! is implemented as calling print! with the result of concat!:

https://github.com/rust-lang/rust/blob/1.23.0/src/libstd/macros.rs#L150-L154

print! just calls format_args:

https://github.com/rust-lang/rust/blob/1.23.0/src/libstd/macros.rs#L119-L121

However, concat! stringifies its argument:

https://github.com/rust-lang/rust/blob/1.23.0/src/libsyntax_ext/concat.rs#L32-L54

This means that the "format string" can be any of:

  • string literal
  • floating point literal
  • character literal
  • integer literal
  • boolean literal
T-libs

Most helpful comment

I do not agree that making a misfeature larger for "consistency" is a good idea.

All 17 comments

Personally, I don't think that println! should accept non-string arguments, but it may be too late to put that particular genie back in the bottle?

One "solution" would be to wrap a dummy concat call in the print! implementation to perform the same coercion.

cc @jseyfried @alexcrichton

The same applies for write / writeln and eprint / eprintln.

Duplicate of #30143. There's not really anything we can do here without breaking back compat.

@sfackler

not really anything we can do here

Why not change print! to add a concat! call so they behave the same?

The writeln/etc behavior is a bug, not a feature.

is a bug, not a feature.

I mean... "bugs" get fixed. This has been declared unfixable, so it's now a "feature". Unless somehow epochs will allow us to undo that mistake, isn't it better to be consistently broken?

I do not agree that making a misfeature larger for "consistency" is a good idea.

https://github.com/rust-lang/rust/pull/41192#issuecomment-300289463

(In general, no one had a clear reason we didn't want this behavior even for print!)

I have a reason: it is really annoying to accidentally leave off format arguments and have the result compile and print your template string. println!("foo: {}").

I'm not understanding your example:

error: 1 positional argument in format string, but no arguments were given
  --> src/main.rs:10:5
   |
10 |     println!("foo: {}");
   |     ^^^^^^^^^^^^^^^^^^^^
   |
   = note: this error originates in a macro outside of the current crate

Oops, I thought I remembered that doing weird things but must have been thinking of something else.

Regardless, though, this is a duplicate of that issue. As far as I know, no one actually intentionally uses this behavior. I don't know why we would go out of our way to extend the support of a weird oddity.

As a more real argument as to why this behavior is bad, it seems better to know that the formatting functions expect a string literal first always, as opposed to a string literal first, unless you want to print a literal value, in which case you can just put it in. If we did anything like this IMO, we'd want println!(x) to be equivalent to println!("{}", x).

So I feel like overall consensus though is that this is a duplicate issue and we don't want to "fix" (i.e., for consistency) print! and also don't want to remove (non backwards compatibly) the concat! from println!. Is that correct? If so, then we should close.

@sfackler : I think you remembered your issue backward. If you use println!(foo) it will print foo (as the string) instead of reminding you that you're missing a format string.

Ah yeah that might have been it.

Closing as duplicate of #30143

Was this page helpful?
0 / 5 - 0 ratings