1. Overview
In this tutorial, we’re going to demonstrate different ways to format output with print!
and println!
macros in Rust.
2. Syntax
Both print!
and println!
macros are defined in the standard library. They are used to print output to the console.
The only difference between them is that println!
adds a newline at the end of the output, while print!
doesn’t.
Let’s look at the most commonly used macro to print output to the console before we dive into the details:
println!("Hello, world!");
This will print Hello, world!
to the console followed by a newline.
3. Basic Formatting
Not like Java or C, we don’t use %
to format output in Rust. Instead, we use placeholders and provide values to be inserted into the string.
The placeholder {} will be replaced by the associated value. Example:
let name = "Alex";
println!("Hello, {}!", name);
This will print Hello, Alex!
to the console.
Positional Arguments
You can specify the position of arguments using numbers. Example:
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
This will print Alice, this is Bob. Bob, this is Alice
to the console.
Named Arguments
You also can provide names for your arguments. Example:
println!("{subject} {verb} {object}",
object="the lazy dog",
subject="the quick brown fox",
verb="jumps over");
This will print the quick brown fox jumps over the lazy dog
to the console.
4. Special Formatting
Rust provides various traits to format output, such as {:?} for Debug trait, {:x} for hexadecimal, {:p} for pointers, etc. The most commonly used one would be the Debug trait, which is used to print debug information about a value.
println!("{:?} is the debug representation", Some("value"));
This will print Some("value") is the debug representation
to the console. Note: Some
already implements the Debug trait, so we don’t need to implement it ourselves.
For structs and enums, you can derive the Debug trait to quickly enable printing using println!
.
Example:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
let point = Point { x: 10, y: 20 };
println!("{:?}", point);
This will print Point { x: 10, y: 20 }
to the console.
5. String formatting: Padding
In Rust, you can specify the formatting options after a :
.
Minimum Width
You can specify the minimum width of the output using a number after :
.
let name = "Alex";
println!("Hello, {:10}!", name);
This will print Hello, Alex !
to the console.
Left Alignment
By default, strings are left-aligned. But you can explicitly specify it using <
after :
.
let name = "Alex";
println!("Hello, {:<10}!", name);
This will still print Hello, Alex !
to the console.
Right Alignment
You can specify right alignment using >
after :
.
let name = "Alex";
println!("Hello, {:>10}!", name);
This will print Hello, Alex!
to the console.
Center Alignment
You can specify center alignment using ^
after :
.
let name = "Alex";
println!("Hello, {:^10}!", name);
This will print Hello, Alex !
to the console.
Custom Padding Character
To pad with a character other than a space, you can combine alignment with a specified character.
Below is an example of padding with =
:
let name = "Alex";
println!("Hello, {:=>10}!", name);
This will print Hello, ======Alex!
to the console.
6. String formatting: Truncating
You can truncate strings using .
after :
.
let name = "Hello, Rust!";
println!("{:.5}", name);
This will print Hello
to the console.
Combine Truncating and Padding
You can combine truncating and padding to get the desired output.
let name = "Hello, Rust!";
println!("{:10.5}", name);
This will print Hello
to the console.
Right Alignment with Truncating
Similarly, you can combine right alignment and truncating.
let name = "Hello, Rust!";
println!("{:=>10.5}", name);
This will print =====Hello
to the console.
7. Number Formatting
You can format numbers using :b
for binary, :o
for octal, :x
for hexadecimal, and :e
for scientific notation.
let number = 255;
println!("Binary: {:b}", number);
println!("Octal: {:o}", number);
println!("Hexadecimal: {:x}", number);
println!("Scientific notation: {:e}", number);
This will print:
Binary: 11111111
Octal: 377
Hexadecimal: ff
Scientific notation: 2.55e2
Precision
For floating-point numbers, you can specify the precision using :.2
after :
.
let number = 3.1415926;
println!("Pi: {:.2}", number);
This will print Pi: 3.14
to the console.
Fixed Point Notation
You can use :.*
to format floating-point numbers in fixed point notation.
let number = 3.1415926;
println!("Pi: {:.*}", 3, number);
This will print Pi: 3.142
to the console.
8. Date and Time Formatting
In Rust, date and datetime formatting is typically provided by the chrono
crate, which is one of the most popular
date and time libraries in Rust ecosystem.
Example:
First, add chrono
to your Cargo.toml
:
[dependencies]
chrono = "0.4"
Then, use chrono
to format date and time:
extern crate chrono;
use chrono::{NaiveDate, Utc, Local, DateTime};
fn main() {
// Current date and time in UTC
let now: DateTime<Utc> = Utc::now();
println!("{}", now.format("%Y-%m-%d %H:%M:%S")); // e.g., "2023-10-23 14:45:34"
// Current date and time in local time
let local_now: DateTime<Local> = Local::now();
println!("{}", local_now.format("%Y-%m-%d %H:%M:%S"));
// Specified date
if let Some(date) = NaiveDate::from_ymd_opt(2023, 10, 20){
println!("{}", date.format("%A, %B %d, %Y"));
}
// e.g., "Friday, October 20, 2023"
}
In the above example, we used format
method to format date and time. The format
method accepts a string as an argument,
which contains the formatting directives. The formatting directives are prefixed with %
and are similar to those used in C’s strftime
function.
%Y
: Year with century as a decimal number.%m
: Month as a decimal number [01,12].%d
: Day of the month as a decimal number [01,31].%H
: Hour (24-hour clock) as a decimal number [00,23].%M
: Minute as a decimal number [00,59].%S
: Second as a decimal number [00,61].%A
: Weekday as locale’s full name.%B
: Month as locale’s full name.
For a complete list of formatting directives, please refer to chrono’s documentation.
9. Conclusion
In this article, we learned how to format output with print!
and println!
macros in Rust.
It’s essential to remember that print! and println! are macros, not functions, hence the ! at the end of their names.
This distinction is vital in Rust because macros can do things that functions can’t, like taking a variable number of arguments.
If you’re coming from languages like Python or Java, Rust’s print macros provide a similar but slightly different approach to formatted output.
It might take a bit of getting used to, but the flexibility and safety they offer are well worth it.