Avatar ·

Как в Rust объявить многострочную строковую литерал? Синтаксис и экранирование

Привет, коллеги! Я только начал изучать Rust и столкнулся с проблемой при работе с длинными строками. Мне нужно объявить строковый литерал, который занимает несколько строк, например, для хранения SQL-запроса, JSON-шаблона или просто длинного текста с переносами. В других языках, например в Python, я могу использовать тройные кавычки `"""..."""` или обратные кавычки. В C# есть `@""` строки. А вот в Rust я попробовал просто перенести строку на новую строку внутри двойных кавычек — компилятор выдаёт ошибку. Вот пример того, что я пытаюсь сделать: ```rust let query = "SELECT * FROM users WHERE age > 18 ORDER BY name"; ``` Компилятор говорит: `error: unterminated double quote string`. Я понимаю, что Rust не любит переносы внутри обычных строк. Но как тогда быть? Мне нужно, чтобы строка сохраняла переносы строк (как в сырых строках), но при этом я не хочу писать всё в одну строку, так как это ухудшает читаемость. Подскажите, пожалуйста, есть ли в Rust специальный синтаксис для многострочных литералов? Или нужно использовать экранирование `\n` и конкатенацию? Если есть несколько способов, какой из них считается идиоматичным? Заранее спасибо за помощь! <<>>

Основной способ: сырые строки с префиксом r

В Rust для многострочных строковых литералов используется синтаксис сырых строк (raw string literals). Он позволяет включать переносы строк и любые символы без экранирования. Базовый синтаксис:

let multi = r"Первая строка
Вторая строка
Третья строка";

Однако в таком виде строка не будет содержать символы переноса строк в самом литерале — они будут интерпретироваться как часть текста. Если вам нужны именно переносы строк (как в примере с SQL), используйте \n внутри сырой строки — но это неудобно. Правильный подход — использовать сырые строки с хешами для гибкости.

Синтаксис с хешами r#"..."#

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

let sql = r#"SELECT * FROM users
WHERE age > 18
ORDER BY name"#;

println!("{}", sql);
// Вывод:
// SELECT * FROM users
// WHERE age > 18
// ORDER BY name

Если внутри строки встречаются символы "#, можно добавить больше хешей: r##"..."##, r###"..."### и так далее. Это решает проблему вложенных кавычек.

Идиоматичный подход для длинных текстов

Для SQL, JSON или конфигов лучше использовать сырые строки с хешами. Пример с JSON:

let json = r#"{
    "name": "Alice",
    "age": 30,
    "city": "New York"
}"#;

Альтернатива: конкатенация строк

Если вам нужно объединить строки без лишних переносов, можно использовать конкатенацию с помощью + или format!:

let long = "Первая часть "
    "вторая часть "
    "третья часть";
// Это работает благодаря конкатенации литералов на этапе компиляции

Обратите внимание: пробел в конце каждой строки сохраняется. Если нужно избежать пробелов, используйте явный \n внутри строк.

Важные замечания

  • Сырые строки не поддерживают escape-последовательности (\n, \t и т.д.) — они воспринимаются буквально. Если вам нужны управляющие символы, используйте обычные строки с \n.
  • Для очень длинных строк (например, HTML-шаблонов) рассмотрите использование макроса include_str! для загрузки содержимого из внешнего файла.
  • В Rust 2021 edition появилась возможность использовать r# внутри макросов, но для литералов это не требуется.

Практический совет

Если вы пишете SQL-запрос, лучше вынести его в отдельный файл и использовать include_str!:

let query = include_str!("query.sql");
// query.sql:
// SELECT * FROM users WHERE age > 18 ORDER BY name;

Это улучшает читаемость и позволяет использовать подсветку синтаксиса SQL в редакторе.

<<>>

Использование макроса concat! для сборки строк

Если вам нужно объединить несколько строковых литералов без лишних пробелов и переносов, можно использовать макрос concat!. Он работает на этапе компиляции и не создаёт промежуточных строк:

let text = concat!(
    "Первая строка",
    "Вторая строка",
    "Третья строка"
);
// Результат: "Первая строкаВторая строкаТретья строка"

Этот способ полезен, когда вы хотите разбить длинную строку на логические части, но не хотите вставлять переносы строк. Если переносы всё же нужны, добавьте "\n" между частями:

let text = concat!(
    "Первая строка",
    "\n",
    "Вторая строка"
);

Когда это лучше сырых строк?

  • Если строка содержит много кавычек или хешей, которые мешают использованию r#"..."#.
  • Если вы хотите явно контролировать, где именно вставляются переносы строк.
  • Для коротких многострочных фрагментов, где читаемость важнее гибкости.

Однако для большинства случаев (SQL, JSON, HTML) сырые строки с хешами остаются более идиоматичным выбором.