Avatar ·

Как экранировать фигурные скобки в строке форматирования в Rust?

Всем привет! Я только начал изучать Rust и столкнулся с проблемой при работе с макросом `format!` (и `println!` тоже). Мне нужно вывести строку, которая содержит буквальные фигурные скобки `{` и `}`. Например, я хочу сгенерировать JSON-подобный вывод или просто отобразить математическое выражение вроде `{x: 5}`. Я попробовал написать так: ```rust let x = 5; println!("{x: {}}", x); ``` Но компилятор выдаёт ошибку: "invalid format string: expected `'}'` but string was terminated". Я понимаю, что фигурные скобки в Rust используются как плейсхолдеры для подстановки значений. Но как мне вывести сами скобки как обычные символы? Пробовал экранировать обратным слешем `\{` — не работает. Пробовал удваивать `{{` — компилятор начал ругаться по-другому. В других языках (Python, C#) я просто удваиваю скобки или использую экранирование. Как это правильно делается в Rust? Мне нужно вывести что-то вроде: - `{ значение: 42 }` - Или сгенерировать JSON: `{"key": "value"}` Подскажите, пожалуйста, как правильно экранировать фигурные скобки в строках форматирования Rust. Желательно с примерами для разных случаев: внутри `format!`, `println!` и при работе с `write!`. Заранее спасибо! <<>>

Экранирование фигурных скобок через удвоение

В Rust для вывода буквальных фигурных скобок в строках форматирования используется простое правило: удвойте скобку. {{ выведет одну открывающую скобку {, а }} — одну закрывающую }. Это работает во всех макросах семейства format!: format!, println!, write!, print! и т.д.

fn main() {
    // Простейший пример: вывод скобок без подстановки
    println!("{{Hello}}"); // Выведет: {Hello}
    
    // Комбинация с подстановкой значения
    let value = 42;
    println!("{{ значение: {} }}", value); // Выведет: { значение: 42 }
    
    // Именованные аргументы
    println!("{{x: {x}}}", x = 10); // Выведет: {x: 10}
    
    // JSON-подобный вывод
    let key = "name";
    let val = "Alice";
    println!(r#"{{"{}": "{}"}}"#, key, val); // Выведет: {"name": "Alice"}
}

Правила экранирования

  • Две открывающие скобки {{ → одна {
  • Две закрывающие скобки }} → одна }
  • Если вам нужно {{ или }} как текст — используйте {{{{ и }}}} (четыре скобки дадут две)

Пример с вложенным форматированием

Предположим, вы хотите сгенерировать строку, где количество фигурных скобок зависит от данных. Например, создание шаблона LaTeX или JSON с динамическими ключами.

fn generate_json(key: &str, value: &str) -> String {
    format!(r#"{{"{}": "{}"}}"#, key, value)
}

fn main() {
    let json = generate_json("status", "ok");
    println!("{}", json); // {"status": "ok"}
    
    // Если нужны двойные фигурные скобки (например, для шаблонизатора)
    let template = format!("{{{{ {} }}}}", "variable");
    println!("{}", template); // {{ variable }}
}

Работа с write! и writeln!

Правило удвоения работает и для макросов, записывающих в поток. Пример с write!:

use std::fmt::Write;

fn main() {
    let mut output = String::new();
    write!(&mut output, "{{x: {}, y: {}}}", 10, 20).unwrap();
    println!("{}", output); // {x: 10, y: 20}
}

Почему не работает обратный слеш?

В Rust строки форматирования не поддерживают escape-последовательности в стиле C (\{). Обратный слеш в форматной строке трактуется буквально или как часть спецификатора формата (например, \n для новой строки). Экранирование фигурных скобок реализовано именно через удвоение, так как это проще парсить и не конфликтует с другими правилами.

Советы для новичков

  • Если вы пишете много скобок подряд, используйте сырые строки (r#"..."#) — они упрощают чтение, но не отменяют правила удвоения внутри форматной строки.
  • Для сложных шаблонов рассмотрите использование крейта serde_json для JSON или специализированных шаблонизаторов.
  • Помните: {{ и }} — это единственный способ. Не пытайтесь экранировать через \ или HTML-сущности.

Если вам нужно вывести ровно одну фигурную скобку — используйте {{ или }}. Для двух — {{{{ или }}}} и так далее. Это интуитивно понятно после пары примеров.

fn main() {
    // Демонстрация всех вариантов
    println!("Одна скобка: {{ и }}");      // Одна скобка: { и }
    println!("Две скобки: {{{{ и }}}}");   // Две скобки: {{ и }}
    
    // Реальный пример: математическая формула
    let a = 3;
    let b = 4;
    println!("{{ {} + {} = {} }}", a, b, a + b); // { 3 + 4 = 7 }
}
<<>>

Альтернативный подход: конкатенация строк

Если вам неудобно запоминать правила удвоения скобок, или вы имеете дело с очень сложным форматированием, можно просто использовать конкатенацию строк с помощью оператора + или макроса concat!. Этот подход полностью обходит механизм форматирования.

fn main() {
    let value = 42;
    
    // Вариант 1: конкатенация через +
    let result = "{ значение: ".to_string() + &value.to_string() + " }";
    println!("{}", result); // { значение: 42 }
    
    // Вариант 2: макрос concat! (только для строковых литералов)
    let key = "x";
    let result2 = concat!("{", key, ": ", "10", "}");
    println!("{}", result2); // {x: 10}
    
    // Вариант 3: format! без экранирования (передаём скобки как обычные строки)
    let left = "{";
    let right = "}";
    println!("{} {} {}", left, value, right); // { 42 }
}

Когда это полезно?

  • При динамическом построении строк с переменным количеством скобок.
  • В коде, где читаемость важнее производительности (конкатенация может быть медленнее format!).
  • При работе с текстом, содержащим много уровней вложенных скобок (например, генерация LaTeX).

Однако учтите, что concat! работает только на этапе компиляции и принимает исключительно строковые литералы. Для переменных используйте format! с экранированием или конкатенацию через +.

// Пример для LaTeX: \frac{1}{2}
let numerator = 1;
let denominator = 2;
// Через format! с экранированием:
println!("\\frac{{{}}}{{{}}}", numerator, denominator); // \frac{1}{2}
// Через конкатенацию:
println!("\\frac{" 
    + &numerator.to_string() 
    + "}{" 
    + &denominator.to_string() 
    + "}"); // \frac{1}{2}