Что такое r#""# в Rust? Полный разбор магического оператора

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что скрывается за таинственным r#""# в Rust? Полное объяснение с кодом

Вы когда-нибудь встречали в коде на Rust странную запись вроде r#"..."# и думали: «Что за магический оператор? Это какой-то секретный синтаксис для избранных?» — вы не одиноки. Тысячи разработчиков задавали этот вопрос на Stack Overflow, и сегодня мы раз и навсегда разберёмся, что это такое, зачем это нужно и как использовать сырые строковые литералы (raw string literals) в Rust.

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

Проблема: когда обычные строки ломают код

Представьте, что вы пишете парсер JSON или регулярное выражение. Внутри строки вам нужно использовать кавычки и обратные слеши. В обычном Rust-коде это выглядит как кошмар:

let json_string = "{\"name\": \"Rust\", \"year\": 2015}";
let regex = "\\d{2}-\\d{2}-\\d{4}"; // дата в формате DD-MM-YYYY

Смотрите на это? Каждый раз, когда вам нужна кавычка внутри строки, вы ставите перед ней обратный слеш (\"). А если вам нужен сам обратный слеш — пишете \\. Код быстро превращается в «лесенку» из слешей, которую трудно читать и отлаживать.

Но это ещё цветочки. А что, если вам нужно вставить в строку символ табуляции? Тогда вы пишете \t. А если вам нужен буквально текст \t? Приходится экранировать: \\t. Глаза начинают болеть уже на второй строке.

Решение: сырые строки — r#""#

Rust предлагает элегантный выход — сырые строковые литералы. Они обозначаются префиксом r и обрамляются в #:

// Сырая строка: всё внутри считается буквальным текстом
let raw_json = r#"{"name": "Rust", "year": 2015}"#;
let raw_regex = r#"\d{2}-\d{2}-\d{4}"#;

Чувствуете разницу? Никакого экранирования! Кавычки внутри — просто кавычки. Обратные слеши — просто слеши. Компилятор Rust воспринимает содержимое между r#" и "# как чистый текст, не пытаясь интерпретировать escape-последовательности.

Это особенно полезно, когда вы работаете с:

  • Регулярными выражениями — они полны слешей и спецсимволов.
  • JSON, XML, HTML — строки с кучей кавычек.
  • SQL-запросами — особенно с вложенными кавычками.
  • Путями файлов в Windows — C:\Users\Name вместо C:\\Users\\Name.
  • Любыми данными, где обратный слеш должен остаться как есть.

Как это работает под капотом?

Когда Rust встречает r#"..."#, он просто копирует всё, что находится между начальным r#" и конечным "#, в строку без какой-либо обработки. Никаких \n не превращается в перенос строки, никакие \t не становятся табуляцией. Это буквальный срез текста.

Но есть нюанс: что делать, если внутри самой строки встречается последовательность "#? Например, вы хотите вставить текст #"hello"#. Если вы напишете r#"#"hello"#"#, компилятор запутается — где заканчивается литерал?

Для этого в Rust придумали множественные хэши. Вы можете использовать r##"..."##, r###"..."### и так далее. Количество # с обеих сторон должно совпадать:

// Строка содержит внутри "# — используем двойные хэши
let tricky = r##"Строка с "# внутри"##;
println!("{}", tricky); // Выведет: Строка с "# внутри

// А если внутри есть "##? Тогда тройные хэши:
let very_tricky = r###"Текст с "## внутри"###;
println!("{}", very_tricky); // Выведет: Текст с "## внутри

Это напоминает матрёшку: чем больше хэшей вы ставите, тем более сложные последовательности можете включить внутрь. На практике редко нужно больше двух-трёх хэшей, но Rust поддерживает до 255 символов #.

Жизненные примеры: где r#""# реально спасает

1. Регулярные выражения без боли

Вспомните наш пример с датой. С сырыми строками регулярка читается как учебник:

use regex::Regex;

fn main() {
    // Сырая строка — никаких двойных слешей!
    let date_regex = Regex::new(r#"\d{2}-\d{2}-\d{4}"#).unwrap();
    let text = "Сегодня 15-03-2024, а вчера было 14-03-2024";
    
    for date in date_regex.find_iter(text) {
        println!("Найдена дата: {}", date.as_str());
    }
}

Без r#""# пришлось бы писать "\\d{2}-\\d{2}-\\d{4}". Согласитесь, разница колоссальная.

2. JSON в коде

Часто нужно захардкодить JSON для тестов или конфигурации:

let config = r#"{
    "server": {
        "host": "127.0.0.1",
        "port": 8080,
        "ssl": false
    },
    "logging": {
        "level": "debug",
        "file": "/var/log/app.log"
    }
}"#;

// Теперь config — это готовая строка JSON, которую можно парсить
let parsed: serde_json::Value = serde_json::from_str(config).unwrap();
println!("Порт сервера: {}", parsed["server"]["port"]);

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

3. Windows-пути

Разработчики на Windows знают, как болят глаза от путей вроде "C:\\Users\\Имя\\Documents\\file.txt". С сырыми строками:

let path = r#"C:\Users\Имя\Documents\file.txt"#;
let file = std::fs::read_to_string(path).expect("Файл не найден");
println!("Содержимое: {}", file);

К

Блоги

Рекомендаций по книгам