Introduction: Why Doesn't Rust Like Curly Braces?
\n\nIf you've ever written in Rust and tried to output something like { "name": "Rust" } via println!, you've probably seen this:
println!("{ "name": "Rust" }"); // Compilation error!\n\nThe compiler gleefully reports: "invalid reference to argument 'name' — no argument named 'name' found". And you're left puzzled: how do you output curly braces in a string? In Python, this is done with {{ }}, but it's simpler there. Rust has its own philosophy: curly braces are not just characters, but part of a powerful formatting system borrowed from Python, but with nuances.
In this article, we'll cover all the ways to escape curly braces in Rust: from the basic {{ }} to tricks with Debug and raw string literals. You'll learn how to output JSON, generate code, and work with templates without falling into compiler traps.
Why Are Curly Braces Special?
\n\nIn Rust, the macros format!, println!, write!, and format_args! use curly braces as placeholder markers. For example:
let name = "Rust";\nprintln!("Hello, {}!", name); // Hello, Rust!\n\nHere, {} is a placeholder that gets replaced with the variable's value. But what if you need the { or } character itself? Rust uses a simple trick: doubling the braces.
Method 1: Classic Doubling {{ and }}
\n\nThe simplest and most obvious way is to write two curly braces in a row. The first pair {{ turns into one {, and }} turns into one }.
Example 1: Simple Escaping
\nfn main() {\n let text = format!("Function main() {{ ... }}");\n println!("{}", text);\n // Output: Function main() { ... }\n}\n\nExample 2: Escaping with Substitution
\nfn main() {\n let lang = "Rust";\n let year = 2015;\n let result = format!("{{ \\"lang\\": \\"{}\\", \\"year\\": {} }}", lang, year);\n println!("{}", result);\n // Output: { "lang": "Rust", "year": 2015 }\n}\n\nThis method works everywhere: in println!, write!, format!. Remember the rule: two braces = one brace.
Method 2: Named Arguments and Escaping
\n\nIf you use named arguments (e.g., {name}), escaping still works, but you need to be careful with the placement of braces.
Example 3: Named Placeholders and Braces
\nfn main() {\n let user = "Alice";\n // We want to output: Hello, {Alice}!\n println!("Hello, {{{}}}!", user);\n // Output: Hello, {Alice}!\n}\n\nLet's break down the string "Hello, {{{}}}!":\n
- \n
{{→ outputs{\n {}→ substitutesuser\n }}→ outputs}\n
Important: the number of braces is always even for escaping. If you have {{{ — that's an error, because three braces do not form a valid pair.
Method 3: Using Debug Format ({:?})
\n\nSometimes you want to output a data structure so that it automatically displays with curly braces. For example, during debugging or JSON generation.
\n\nExample 4: Outputting a Structure via Debug
\n#[derive(Debug)]\nstruct Point {\n x: i32,\n y: i32,\n}\n\nfn main() {\n let p = Point { x: 10, y: 20 };\n println!("{:?}", p);\n // Output: Point { x: 10, y: 20 }\n}\n\nThe Debug trait automatically adds braces around the fields. This is convenient if you need to quickly display the state of an object. But if you need exactly JSON or another