Introducción: ¿por qué Rust no ama las llaves?
\n\nSi alguna vez has escrito en Rust e intentado mostrar algo como { "name": "Rust" } a través de println!, seguramente viste esto:
println!("{ "name": "Rust" }"); // ¡Error de compilación!\n\nEl compilador informa con sorna: "invalid reference to argument 'name' — no argument named 'name' found". Y te quedas perplejo: ¿cómo se muestran las llaves en una cadena? En Python se hace con {{ }}, pero allí es más simple. En Rust hay su propia filosofía: las llaves no son solo símbolos, sino parte de un potente sistema de formato, tomado de Python, pero con matices.
En este artículo analizaremos todas las formas de escapar llaves en Rust: desde el básico {{ }} hasta trucos con Debug y raw string literals. Aprenderás a mostrar JSON, generar código y trabajar con plantillas sin caer en las trampas del compilador.
¿Por qué las llaves son especiales?
\n\nEn Rust, las macros format!, println!, write! y format_args! usan las llaves como marcadores de sustitución. Por ejemplo:
let name = "Rust";\nprintln!("¡Hola, {}!", name); // ¡Hola, Rust!\n\nAquí {} es un placeholder que se reemplaza por el valor de la variable. Pero, ¿qué pasa si necesitas el propio símbolo { o }? Rust usa un truco simple: duplicar las llaves.
Método 1: Duplicación clásica de {{ y }}
\n\nLa forma más simple y obvia es escribir dos llaves seguidas. El primer par {{ se convierte en una {, y }} en una }.
Ejemplo 1: Escapado simple
\nfn main() {\n let text = format!("Función main() {{ ... }}");\n println!("{}", text);\n // Salida: Función main() { ... }\n}\n\nEjemplo 2: Escapado con sustitución
\nfn main() {\n let lang = "Rust";\n let year = 2015;\n let result = format!("{{ \\"lang\\": \\"{}\\", \\"year\\": {} }}", lang, year);\n println!("{}", result);\n // Salida: { "lang": "Rust", "year": 2015 }\n}\n\nEste método funciona en todas partes: en println!, write!, format!. Recuerda la regla: dos llaves = una llave.
Método 2: Argumentos nombrados y escapado
\n\nSi usas argumentos nombrados (por ejemplo, {name}), el escapado también funciona, pero hay que tener cuidado con la posición de las llaves.
Ejemplo 3: Placeholders nombrados y llaves
\nfn main() {\n let user = "Alice";\n // Queremos mostrar: ¡Hola, {Alice}!\n println!("¡Hola, {{{}}}!", user);\n // Salida: ¡Hola, {Alice}!\n}\n\nAnalicemos la cadena "¡Hola, {{{}}}!":\n
- \n
{{→ muestra{\n {}→ sustituyeuser\n }}→ muestra}\n
Importante: la cantidad de llaves siempre debe ser par para el escapado. Si tienes {{{ es un error, ya que tres llaves no forman un par correcto.
Método 3: Uso del formato Debug ({:?})
\n\nA veces quieres mostrar una estructura de datos para que aparezca automáticamente con llaves. Por ejemplo, al depurar o generar JSON.
\n\nEjemplo 4: Salida de estructura mediante 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 // Salida: Point { x: 10, y: 20 }\n}\n\nEl trait Debug añade automáticamente llaves alrededor de los campos. Es útil si necesitas mostrar rápidamente el estado de un objeto. Pero si necesitas exactamente JSON u otro