Println!() is needlessly lacking

println!() macro seems to be needlessly verbose. One doesn't have to specify a formatter string to be able to print a non-static &str or String, or any T: std::fmt::Display. What's the rationale behind keeping println!() so primitive?

I'm taking this opportunity to suggest extending println!() thus below:

macro_rules! putln {
    ($($arg:expr),*) => {
        $(print!("{}", $arg);)*
        println!();
    };
}

macro_rules! putstr {
    ($($arg:expr),*) => {
        $(print!("{}", $arg);)*
    };
}

fn main() {
    {
        let greeting = "Hello";
        putstr!(greeting);  // takes non-static &str
    }
    putln!();
    putln!(String::from("World"), " ", 123i32);  // takes String and i32
}

puts!()and println!() are different, puts!() will not format arguments.

I'm not sure what you mean. That seems to be more primitive, like python's print() that's only capable of doing __str__ formatting.

Actually, rust's println! is basically identical in spirit to python's print("...".format(...)):

println!("{:3} + {:3} = {:4}", x, y, x + y);
print("{:3} + {:3} = {:4}".format(x, y, x + y))

Granted, I do miss string interpolation from other languages, but that's clearly not what you're asking for...

1 Like

I mean println!() cannot print String or i32 or anything but &static str. println!() is indeed just similar to printf() but I'm saying it can be extended to take plain Strings or i32s. Today this is not allowed

println!(33);
let to_print = String::from("Something");
println!(to_print);

iirc the restrictions on println allow for the format specifiers to be checked at compile time.

Is your recommendation that prrintn! implicitly insert a "{} " per parameter when a format specifier isn't defined?

Could your clarify what the use case is you are trying to handle? For example, I can't imagine printing an i32 without a format specifier except for debugging which is instead serviced by dbg!.

3 Likes

I mean println!() cannot print String or i32 or anything but &static str .

It can't print &'static str either.

let x: &'static str = "abc";
println!(x); // error: format argument must be a string literal

You can't even say it prints string literals the same way you are expecting it to print Strings.

println!("{{"); // prints { instead of {{

The first argument is not a thing to be printed. It is a format template, plain and simple. The fact that you can write println!("Hello, world!"); is just a happy accident stemming from the fact that this format template is allowed to contain arbitrary text. (modulo braces)

2 Likes

... Pretty much, yes. [EDIT: see below, puts!() macro should not do any formatting:

println!("a + b = ", 4, 2);  // equivalent to println!("{}{}{}", "a + b = ", 4, 2); ?
println!("{} + {} = 6", 4, 2);  // equivalent to println!("{} + {} = 6", 4, 2); ?

use cases

It should be the same use cases as puts() and more. Sometimes users may just want to print a String or any type that implements std::fmt::Display. C provides both printf() and puts(). I'm saying that printf() can be extended to emulate the functionality of puts() to some extent or even more.

If you had println! do this automatically, by the way, it's either going to be ambiguous or highly contextual.

println!("a + b = ", 4, 2);  // equivalent to println!("{}{}{}", "a + b = ", 4, 2); ?
println!("{} + {} = 6", 4, 2);  // equivalent to println!("{} + {} = 6", 4, 2); ?
1 Like

puts() is actually different to printf(), it doesn't format strings.

println!("{} + {} = 6", 4, 2);  // "4 + 2 = 6"
puts!("{} + {} = 6", 4, 2);  // "{} + {} = 642"

I should really retract my previous statement of implicitly putting {} for each argument, of course the implementation will probably need that but at the user-level, {} should not do any formatting, println!() exists for that.

Should puts! include all of the kwargs that Python3's print() supports (sep, end, file, flush)? Should there be versions that take an io::Write (like write! instead of print!) and don't take a format string?

Having just the versions taking format strings is simpler because there's only one way to do it. And it compiles to successive writes, there's no formatting overhead at runtime.