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