Print

Dear Computer

Chapter 10: Everything Revisited

Print

Before we can write hello_world.rs, we need to know how to output data. In Rust, we use println!, which does formatted printing like printf in Java and C. Rather than % placeholders, the format string uses curly brace placeholders:

Rust
fn main() {
  // prints 3 + 5 = 8
  println!("{} + {} = {}", 3, 5, 3 + 5);
}
fn main() {
  // prints 3 + 5 = 8
  println!("{} + {} = {}", 3, 5, 3 + 5);
}

Each {} is replaced by the string representation of the next positional parameter. To get more control over the format or to select a different parameter, we insert commands (which adhere to a grammar) within the braces. Roughly, each placeholder has this grammatical form:

{selector:formatting}
{selector:formatting}

The selector may be omitted if we are dropping in parameters in positional order. The separator : and the formatting flags may be omitted if we are satisfied with the default formatting.

The following calls demonstrate how we might express selectors and common formatting parameters:

Rust
println!("{1} {0}", 'x', 'y'); // prints "y x"
println!("{:<7}", 0);          // prints "0      "
println!("{:>7}", 0);          // prints "      0"
println!("{:^7}", 0);          // prints "   0   "
println!("{:_^7}", 0);         // prints "___0___"
println!("{:.4}", 0.123);      // prints "0.1230"
println!("{:>9.4}", 0.123);    // prints "   0.1230"
println!("{:?}", (5, true));   // prints "(5, true)"
println!("{1} {0}", 'x', 'y'); // prints "y x"
println!("{:<7}", 0);          // prints "0      "
println!("{:>7}", 0);          // prints "      0"
println!("{:^7}", 0);          // prints "   0   "
println!("{:_^7}", 0);         // prints "___0___"
println!("{:.4}", 0.123);      // prints "0.1230"
println!("{:>9.4}", 0.123);    // prints "   0.1230"
println!("{:?}", (5, true));   // prints "(5, true)"

Not all types have string representations. For example, if we had plain {} on the last call, which prints a tuple, the code wouldn't compile. Tuples don't have string representations. However, most types do have a debugging representation that we request with the ? formatting flag.

The first parameter to println! must always be a literal string because the compiler typechecks the placeholders statically to ensure that they align with their corresponding parameters.

An identifier that ends in ! is a macro, not a function. A macro is shorthand code that is expanded to other source code at compile time—not at runtime. We have seen in C and C++ the #include macro, which expands to the contents of the included file. The println! macro expands to some complex code that actually does the output. There are several reasons println! is a macro rather than a regular function? For one, it's variadic, allowing an arbitrary parameters. Rust functions can't be variadic. For two, the selectors in {} can references variables from the local scope. If println! triggered a function call, it would operate in a different scope.

← Rust as a LensVariables →