Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
122: String Formatting with format! Macro
A few years ago, I was reviewing a PR from a developer who was building a custom logging system for a high-frequency trading platform. He had written a line of code to generate a transaction ID string that looked something like this: let id = String::from("TXN-") + ×tamp.to_string() + &"-" + &user_id.to_string() + &"-" + &session_id.to_string();. It was a complete eyesore. Not only was it hard to read, but it was inefficient, creating multiple intermediate string allocations just to get one final result. I told him right then: "Stop fighting the types and just use the format! macro."
Creating Strings without the Concatenation Headache
If you've used println!, you already know 90% of how format! works. The only difference is that while println! sends the formatted text to the console, format! returns a String. It's your primary tool for building dynamic strings that you need to store in a variable, pass into a function, or return from a method.
let user = "Alice";
let login_attempts = 3;
let message = format!("User {} has failed login {} times.", user, login_attempts);
// message is now a String: "User Alice has failed login 3 times."
I prefer this approach because it separates the structure of the string from the data. It's much easier to scan a template and see what the final output will look like than to mentally parse a chain of plus signs and to_string() calls.
Controlling the Layout with Formatting Specifiers
Sometimes you need more than just a placeholder. If you're building a CLI tool or a report, you likely need things to line up. Rust gives you a powerful set of formatting specifiers inside the curly braces. For example, if you're displaying a price, you don't want 12.5; you want 12.50.
let price = 12.5;
let formatted_price = format!("Total cost: ${:.2}", price);
// Result: "Total cost: $12.50"
You can also handle padding and alignment. If you're printing a list of IDs and want them all to be the same width—say, 5 characters, padded with zeros—you can use {:05}. I've found this incredibly useful when generating filenames for sequential backups or log rotations where alphabetical sorting depends on the numbers having the same number of digits.
Reusing Arguments and Named Placeholders
In more complex strings, you might find yourself needing the same variable three or four times. Instead of passing the same variable into the macro repeatedly, you can use positional arguments. By putting a number inside the braces, you tell Rust which argument to use (starting at 0).
let first = "Hydrogen";
let second = "Helium";
let sequence = format!("{0}, {1}, then {0} again", first, second);
// Result: "Hydrogen, Helium, then Hydrogen again"
But if the list of arguments gets long, numbers become confusing. That's where named arguments come in. This is the cleanest way to handle long templates. You define the name inside the brace and assign the value at the end of the macro call. It makes the code self-documenting.
let report = format!(
"System Status: {status} | Uptime: {uptime}s | Load: {load}%",
status = "Healthy",
uptime = 3600,
load = 14.2
);📋 Practical Task
Build a Digital Invoice Line Generator
You are building a billing module for a freelance platform. Instead of printing directly to the screen, you need to create a function that generates a single, perfectly formatted string for an invoice line item.
Write a function generate_invoice_line(item_name: &str, quantity: u32, unit_price: f64) -> String that returns a string based on these requirements:
- The
item_nameshould be left-aligned and occupy exactly 20 characters. - The
quantityshould be right-aligned and occupy 5 characters. - The
unit_pricemust be formatted to exactly 2 decimal places. - The final string should look like this:
"Item: [Name] Qty: [Qty] Price: $[Price]"
Example Output:
generate_invoice_line("Rust eBook", 2, 19.99) should return:
"Item: Rust eBook Qty: 2 Price: $19.99"
There are no comments for now.