Printing is handled by a series of macros defined in std::fmt which include:
format!write formatted text toStringprint!same asformat!, but print toio::stdoutprintlnsame asprint!, but append a newlineeprint!same asprint!, but useio::stderreprintln!same aseprint!, but append a newline
Formatting
Rust checks formatting correctness at compile time.
// In general, the `{}` will be automatically replaced with any
// arguments. These will be stringified.
println!("{} days", 31);
// Positional arguments can be used. Specifying an integer inside `{}`
// determines which additional argument will be replaced. Arguments start
// at 0 immediately after the format string.
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
// As can named arguments.
println!("{subject} {verb} {object}",
object="the lazy dog",
subject="the quick brown fox",
verb="jumps over");
// Different formatting can be invoked by specifying the format character
// after a `:`.
println!("Base 10: {}", 69420); // 69420
println!("Base 2 (binary): {:b}", 69420); // 10000111100101100
println!("Base 8 (octal): {:o}", 69420); // 207454
println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c
println!("Base 16 (hexadecimal): {:X}", 69420); // 10F2C
// You can right-justify text with a specified width. This will
// output " 1". (Four white spaces and a "1", for a total width of 5.)
println!("{number:>5}", number=1);
// You can pad numbers with extra zeroes,
println!("{number:0>5}", number=1); // 00001
// and left-adjust by flipping the sign. This will output "10000".
println!("{number:0<5}", number=1); // 10000
// You can use named arguments in the format specifier by appending a `$`.
println!("{number:0>width$}", number=1, width=5);
// Rust even checks to make sure the correct number of arguments are used.
println!("My name is {0}, {1} {0}", "Bond");
// FIXME ^ Add the missing argument: "James"
// Only types that implement fmt::Display can be formatted with `{}`. User-
// defined types do not implement fmt::Display by default.
#[allow(dead_code)] // disable `dead_code` which warn against unused module
struct Structure(i32);
// This will not compile because `Structure` does not implement
// fmt::Display.
// println!("This struct `{}` won't print...", Structure(3));
// TODO ^ Try uncommenting this line
// For Rust 1.58 and above, you can directly capture the argument from a
// surrounding variable. Just like the above, this will output
// " 1", 4 white spaces and a "1".
let number: f64 = 1.0;
let width: usize = 5;
println!("{number:>width$}");
Custom types
fmt::DebugUses the{:?}marker. Format text for debugging purposes.fmt::DisplayUses the{}marker. Format text in a more elegant, user friendly fashion.
All types which want to use std::fmt formatting traits require an
implementation to be printable. Automatic implementations are only provided for
types in the std library. All other types must be manually printed somehow.
The fmt::Debug trait makes this easy. All types can derive (automatically
create) the fmt::Debug implementation. This is not true for fmt::Display
which must be manually implemented.
#[derive(Debug)]
struct Structure(i32);
fn main() {
println!("{:?} will now print!", Structure(3));
}
Pretty Printing
You can implement fmt::Display to avoid needing to use fmt::Debug:
use std::fmt;
struct Structure(i32);
impl fmt::Display for Structure {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
fn main() {
println!("{}", Structure(10))
}
dbg!
Instead of println!() it's often nicer to use dbg!() which will also add the
filename and line number.
You still need to add #[derive(Debug)] above your type!
It takes ownership of of an expression (as opposed to println!, which takes a
reference). It returns this ownership, but this is something to keep in mind.
Here we can put dbg! around the expression 30 * scale and, because dbg!
returns ownership of the expression’s value, the width field will get the same
value as if we didn’t have the dbg! call there. We don’t want dbg! to take
ownership of rect1, so we use a reference to rect1 in the next call.
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let scale = 2;
let rect1 = Rectangle {
width: dbg!(30 * scale),
height: 50,
};
dbg!(&rect1);
}