Avatar ·

Что означает оператор r#""# в Rust и как использовать сырые строковые литералы?

Привет! Я начал изучать Rust недавно и столкнулся с непонятной конструкцией в чужом коде. Там было написано что-то вроде: ```rust let pattern = r#"^\d+\.\d+\.\d+\.\d+$"#; let json = r#"{"name": "John", "age": 30}"#; ``` Я вижу, что перед строкой стоит буква `r`, а затем символы `#"` и `"#`. Что это вообще значит? Я знаю, что в Rust строковые литералы обозначаются двойными кавычками `"..."`, а символ `\` используется для экранирования. Но тут какая-то магия с `#`. Меня особенно смущает, что внутри этой строки есть обычные двойные кавычки. В обычной строке мне пришлось бы писать `\"` для экранирования, но здесь кавычки стоят как есть. И ещё эти решётки `#` по краям — зачем они нужны? Можно ли обойтись без них? Я пробовал написать просто `r"..."`, но компилятор ругается, если внутри строки есть кавычки. А с `r#"..."#` всё работает. Почему так? И что будет, если мне нужно использовать `"#` прямо внутри строки? Например, если я пишу код, который генерирует JSON или регулярное выражение, содержащее кавычки и обратную косую черту. Объясните, пожалуйста, подробно, как работает этот оператор и когда его стоит применять. Мне кажется, это очень полезная штука для работы с многострочными текстами и сырыми данными, но я хочу понять её до конца. <<>>

Что такое сырые строковые литералы в Rust?

Конструкция r#"..."# в Rust — это сырой строковый литерал (raw string literal). Буква r означает "raw" (сырой), а символы # используются как разделители. Главная особенность сырых строк: внутри них не работают escape-последовательности (\n, \", \\ и т.д.). Всё, что вы пишете между r#" и "#, интерпретируется буквально — как есть.

Зачем нужны решётки #?

Если вам нужно использовать двойные кавычки внутри сырой строки, простой синтаксис r"..." не подойдёт — компилятор не поймёт, где заканчивается строка. Решётки решают эту проблему:

// Без решёток — ошибка!
// let error = r"Он сказал: "Привет""; 

// С решётками — работает!
let ok = r#"Он сказал: "Привет""#; 
println!("{}", ok); // Выведет: Он сказал: "Привет"

Если внутри строки встречается последовательность "#, вы можете увеличить количество решёток:

// Текст содержит "# — используем ##
let text = r##"Внутри есть "# и ещё кавычки: "##;
println!("{}", text); 
// Выведет: Внутри есть "# и ещё кавычки: "

Правило простое: количество решёток на границах должно быть больше, чем количество повторяющихся "# внутри строки. Можно использовать r###"..."###, r####"..."#### и так далее.

Практические примеры использования

  • Регулярные выражения — не нужно экранировать обратные слеши:
    let ip_regex = r#"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"#;
    let email_regex = r#"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"#;
  • JSON, XML, HTML — встраивание данных без экранирования:
    let json = r#"{
        "name": "Alice",
        "age": 30,
        "city": "Москва"
    }"#;
  • Многострочные тексты — сохраняются все пробелы и переносы строк:
    let poem = r#"В небе тают облака,
    И, лучистая на зное,
    В искрах катится река,
    Словно зеркало стальное"#;
  • Пути в Windows — обратные слеши не нужно дублировать:
    let path = r#"C:\Users\Admin\Documents\project"#;

Сравнение с обычными строками

// Обычная строка — нужно экранировать
let normal = "Он сказал: \"Привет!\"\nНовая строка.";
// Сырая строка — всё проще
let raw = r#"Он сказал: "Привет!"
Новая строка."#;

Как видите, сырые строки делают код чище и читаемее, особенно когда много спецсимволов. Единственный минус — нельзя использовать escape-последовательности. Если вам нужно вставить символ табуляции \t или Unicode-символ \u{00A9}, придётся использовать обычную строку.

Важное замечание про Unicode

Сырые строки поддерживают Unicode так же, как обычные. Вы можете писать русские буквы, эмодзи и любые другие символы напрямую:

let hello = r#"Привет, мир! 🌍"#;
println!("{}", hello); // Привет, мир! 🌍
<<>>

Альтернативный взгляд: когда сырые строки не нужны

Хотя сырые строки очень удобны, не стоит злоупотреблять ими. Иногда обычные строки с экранированием читаются лучше, особенно если внутри мало спецсимволов:

// Сырая строка может сбивать с толку
let raw = r#"Hello\nWorld"#; // Выведет буквально "Hello\nWorld"
// Обычная строка — понятно, что будет перенос
let normal = "Hello\nWorld"; // Выведет Hello, затем World с новой строки

Также помните, что сырые строки не поддерживают интерполяцию — в Rust для этого есть макрос format! или println!. Если вам нужно вставить переменную в строку, используйте форматирование:

let name = "Анна";
let greeting = format!(r#"Привет, {}!"#, name);
println!("{}", greeting); // Привет, Анна!

И последнее: для работы с регулярными выражениями в Rust лучше использовать крейт regex, который предоставляет макрос regex!, автоматически проверяющий корректность выражения на этапе компиляции. Но для простых случаев или прототипирования сырые строки — отличный выбор.

В целом, правило простое: если строка содержит много обратных слешей или кавычек — используйте r#"..."#. Если нужно экранирование — используйте обычные строки. А если сомневаетесь, спросите себя: "Что будет легче читать через месяц?".