Question
I want to write formatted output in Rust that looks like this:
write!(f, "{ hash:{}, subject: {} }", self.hash, self.subject)
However, curly braces have special meaning in Rust formatting macros, so the outer braces cannot be written directly like that.
I tried escaping them with backslashes:
write!(f, "\{ hash:{}, subject: {} \}", self.hash, self.subject)
That did not work either. Then I found documentation suggesting that literal {, }, or # characters may be escaped, and because \ is already an escape character in Rust strings, I also tried:
write!(f, "\\{ hash:{}, subject: {} \\}", self.hash, self.subject)
That still does not work. How do you correctly include literal curly braces in a Rust format string?
Short Answer
By the end of this page, you will understand how Rust format strings treat curly braces, how to print literal { and } characters, and why backslashes are not the correct solution. You will also see practical examples using write!, format!, and println!.
Concept
In Rust, formatting macros such as println!, format!, and write! use curly braces as placeholders.
For example:
println!("Hello, {}", name);
Here, {} tells Rust where to insert a value.
Because braces are part of the formatting syntax, Rust does not treat them like normal characters inside a format string. If you want to print a literal brace, you must escape it using doubled braces, not backslashes.
- Use
{{to print{ - Use
}}to print}
So this:
write!(f, "{{ hash:{}, subject: {} }}", self.hash, self.subject)
produces output like:
{ hash:123, subject: Rust }
Why this matters
Formatting is used everywhere in Rust:
- printing to the console
- building strings
- writing
Displayimplementations - logging
- error messages
Knowing how formatting syntax works helps you avoid confusing compiler errors and produce clean output correctly.
Mental Model
Think of a Rust format string as a template with special slots.
{}means: “put a value here”{{means: “I really want an actual{character”}}means: “I really want an actual}character”
It is similar to quoting a special character in a mini-language. In Rust formatting, braces belong to the formatter itself, so you must tell Rust when a brace is part of the output instead of part of the formatting instruction.
Syntax and Examples
The key rule is simple:
"{{" // prints {
"}}" // prints }
Basic example
fn main() {
let hash = 42;
let subject = "Rust";
println!("{{ hash:{}, subject: {} }}", hash, subject);
}
Output:
{ hash:42, subject: Rust }
Using format!
fn main() {
let name = "Alice";
let message = format!("User record: {{ name: {} }}", name);
println!("{}", message);
}
Using write! in a Display implementation
use std::fmt;
struct Item {
hash: u32,
subject: String,
}
impl fmt::Display for Item {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{ hash:{}, subject: {} }}", self.hash, self.subject)
}
}
This is the correct form for your original use case.
Why backslashes do not work
Backslashes escape characters in Rust string literals, but formatting braces are interpreted later by the formatting system.
So these are different layers:
- Rust string escaping:
\n,\t,\" - Format string escaping:
{{,}}
Backslashes do not escape formatting placeholders.
Step by Step Execution
Consider this example:
fn main() {
let hash = 7;
let subject = "math";
let text = format!("{{ hash:{}, subject: {} }}", hash, subject);
println!("{}", text);
}
Step by step
-
Rust sees the format string:
"{{ hash:{}, subject: {} }}" -
The formatter reads
{{and turns it into a literal{. -
It then reads the first
{}and insertshash, which is7. -
It continues through the text
, subject:. -
It reads the second
{}and insertssubject, which is"math". -
It reads
}}and turns it into a literal}. -
The final string becomes:
{ hash:7, subject: math } -
println!prints that final string.
Trace view
| Format part | Meaning | Result |
|---|---|---|
{{ | literal { | { |
hash: | plain text | hash: |
{} | insert hash | 7 |
, subject: | plain text | , subject: |
{} | insert subject | math |
}} | literal } | } |
Real World Use Cases
Literal braces appear often in real Rust programs.
1. Custom Display output
When implementing Display for a struct, you may want object-like output:
write!(f, "{{ id:{}, name:{} }}", self.id, self.name)
2. Logging structured-looking messages
println!("event={{ type:{}, status:{} }}", event_type, status);
3. Generating JSON-like text for debugging
This is not real JSON serialization, but sometimes useful for quick debug output:
println!("{{ \"ok\": {}, \"count\": {} }}", ok, count);
4. Building configuration snippets
let line = format!("section {{ key = {} }}", value);
5. Template-style output
If your output language uses braces, such as config formats or DSLs, doubled braces are common in format strings.
Real Codebase Usage
In real projects, developers often combine escaped braces with common formatting patterns.
Guarded formatting
Only include a section when data exists:
if let Some(tag) = &self.tag {
write!(f, "{{ id:{}, tag:{} }}", self.id, tag)
} else {
write!(f, "{{ id:{} }}", self.id)
}
Error messages with structure
eprintln!("Parse error {{ line:{}, column:{} }}", line, column);
Debug-style manual formatting
Sometimes developers want a custom output format instead of using #[derive(Debug)].
write!(f, "User {{ id:{}, active:{} }}", self.id, self.active)
Prefer serializers when output must be strict
If you need actual JSON, developers usually use serde_json instead of manually formatting braces and quotes.
// Better for real JSON than manual string formatting
Common pattern: readable structured logs
Developers often format values in a predictable shape so logs are easy to scan and search.
Common Mistakes
Here are the most common mistakes beginners make.
Mistake 1: Using single braces literally
Broken code:
println!("{ name: {} }", "Alice");
Why it fails:
- Rust thinks
{ name: {} }is formatting syntax, not plain text.
Fix:
println!("{{ name: {} }}", "Alice");
Mistake 2: Using backslashes to escape braces
Broken code:
println!("\{ name: {} \}", "Alice");
Why it fails:
- Backslashes escape characters in Rust strings, not formatting placeholders.
- The formatting system still sees
{and}as special.
Fix:
println!("{{ name: {} }}", "Alice");
Mistake 3: Forgetting that format!, write!, and println! all use the same rule
Broken assumption:
- “Maybe
write!works differently.”
It does not. All Rust formatting macros use doubled braces for literal braces.
Mistake 4: Manually building structured output when a serializer is better
Possible but fragile:
format!("{{ \"name\": \"{}\" }}", name)
Why to be careful:
- special characters in
namemay break the output - real JSON should usually be produced by a serializer
Mistake 5: Confusing Debug and custom formatting
If you just want a quick struct representation, this may be easier:
#[derive(Debug)]
struct Item {
hash: u32,
subject: String,
}
fn main() {
let item = Item { hash: 1, subject: "Rust".to_string() };
println!("{:?}", item);
}
Use manual brace formatting when you want exact output control.
Comparisons
| Situation | Correct approach | Example |
|---|---|---|
| Print a value | Use {} | println!("{}", value); |
Print a literal { | Use {{ | println!("{{"); |
Print a literal } | Use }} | println!("}}"); |
| Escape a quote in a Rust string | Use \" | println!("\"hi\""); |
| Escape a newline in a Rust string | Use \n | println!("line1\nline2"); |
Backslash escaping vs format escaping
| Type | Used for | Example |
|---|---|---|
| Rust string escaping | Characters inside string literals | \n, \t, \" |
| Format escaping | Literal formatting braces | {{, }} |
Manual formatting vs Debug
| Approach | Best for | Example |
|---|---|---|
Manual write! formatting | Exact custom output | write!(f, "{{ id:{} }}", id) |
Debug with {:?} | Quick developer-facing output | println!("{:?}", value) |
Cheat Sheet
// Literal braces in Rust format strings
"{{" // prints {
"}}" // prints }
// Normal placeholder
"{}" // inserts a value
Correct pattern
write!(f, "{{ hash:{}, subject: {} }}", self.hash, self.subject)
Rules
{}inserts a value{{prints{}}prints}- Backslashes do not escape formatting braces
- The same rule applies to:
println!print!format!write!writeln!
Quick examples
println!("{{}}"); // prints {}
println!("{{{}}}", 5); // prints {5}
println!("x = {}", 10); // prints x = 10
Edge case
This works:
println!("{{{}}}", "abc");
Output:
{abc}
FAQ
How do I print a literal { in Rust?
Use {{ inside a format string.
How do I print a literal } in Rust?
Use }} inside a format string.
Why does \{ not work in println! or write!?
Because backslashes escape Rust string characters, not formatting placeholders. Format braces must be escaped with doubled braces.
Does this rule only apply to println!?
No. It also applies to format!, write!, writeln!, and related formatting macros.
What is the correct version of my original code?
write!(f, "{{ hash:{}, subject: {} }}", self.hash, self.subject)
Should I manually format JSON with braces like this?
Only for simple debug-like output. For real JSON, use a serializer such as serde_json.
Is {:?} related to escaped braces?
Not directly. {:?} is a formatting placeholder for Debug output, but it still uses the same brace-based formatting system.
Mini Project
Description
Create a small Rust program that prints user records in a custom structured format using formatting macros. This helps you practice inserting values with {} while printing literal braces with {{ and }}.
Goal
Build a program that formats and prints a few records like { id:1, name: Alice } correctly.
Requirements
- Define a struct to represent a user record.
- Create at least two example records.
- Print each record in a custom brace-based format.
- Use Rust formatting macros instead of manual string concatenation.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.