Склеиваем строки в Rust: ТОП-5 ошибок новичков (и как их избежать)

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

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

Начать курс

Введение: Почему в Rust нельзя просто сложить строки?

Если вы перешли на Rust из Python или JavaScript, то первое, что вас, скорее всего, шокировало — это попытка написать "Hello, " + "world!". Компилятор встречает вас красной волной и загадочным сообщением об ошибке. В мире Python такое сработало бы мгновенно, но Rust — это другой зверь. Вопрос "How do I concatenate strings?" на Stack Overflow набрал сотни тысяч просмотров, и это не случайно.

Проблема в том, что конкатенация строк в Rust — это не просто синтаксический сахар. Это вопрос управления памятью, владения (ownership) и типов данных. Если вы новичок, вы легко можете наступить на классические грабли: потерять владение строкой, случайно скопировать гигабайты данных или просто запутаться в String и &str.

В этой статье мы разберём 5 самых частых ошибок, которые совершают новички при склеивании строк в Rust, и покажем, как их избежать. Вы узнаете, когда использовать +, когда format!, а когда push_str. Поехали!

Ошибка №1: Забыть про взятие владения (ownership)

Как это выглядит

Представьте, что вы пишете код:

let s1 = String::from("Привет");
let s2 = String::from(", мир!");
let s3 = s1 + &s2; // Ошибка? Или нет?
println!("{}", s1); // Ошибка компиляции!

Вы ожидаете, что s1 останется доступным, но компилятор говорит: "Нет, s1 был перемещён (moved)".

В чём суть ошибки

В Rust оператор + для строк использует сигнатуру fn add(self, s: &str) -> String. Обратите внимание на self — это значит, что первая строка потребляется (consumes) и вы больше не можете её использовать. Вы буквально "отдаёте" s1 функции add, и она становится недоступной.

Как исправить

Есть два пути:

  • Клонировать строку (если вам нужна исходная): let s3 = s1.clone() + &s2;
  • Изменить порядок или использовать format!:
let s1 = String::from("Привет");
let s2 = String::from(", мир!");
let s3 = format!("{}{}", s1, s2); // s1 и s2 остаются доступными
println!("{}", s1); // Работает!

Совет: Если вы не планируете использовать s1 после конкатенации, смело используйте +. Это быстрее, чем format!, так как не создаёт новый буфер с нуля.

Ошибка №2: Путаница между String и &str

Как это выглядит

Вы пытаетесь сложить String с литералом:

let s = String::from("Hello");
let result = s + " world"; // Работает!

Но если вы попробуете наоборот:

let s = "Hello";
let result = s + String::from(" world"); // Ошибка!

В чём суть ошибки

Оператор + работает только когда левая часть — это String, а правая — &str. Литерал " world" автоматически является &str, поэтому первый пример работает. Во втором примере s — это &str, у которого нет метода add.

Как исправить

Преобразуйте &str в String или используйте format!:

let s = "Hello";
let result = format!("{} {}", s, "world"); // Универсальный способ
// Или
let result = s.to_string() + " world"; // to_string() создаёт String

Золотое правило: String — это владеющий тип (owned), &str — это заимствование (borrow). Конкатенация всегда требует владения слева.

Ошибка №3: Игнорирование производительности

Как это выглядит

Вы склеиваете 1000 строк в цикле:

let mut result = String::new();
for i in 0..1000 {
    result = result + &i.to_string(); // Медленно!
}

В чём суть ошибки

Каждый раз при использовании + или format! создаётся новая строка, а старая удаляется. Это приводит к множественным аллокациям памяти и копированию данных. Для больших объёмов это катастрофа по скорости.

Как исправить

Используйте push_str или push для добавления в существующую строку:

let mut result = String::with_capacity(1000 * 10); // Заранее выделяем память
for i in 0..1000 {
    result.push_str(&i.to_string());
}

Бонус: Метод with_capacity позволяет заранее зарезервировать память, чтобы избежать лишних реаллокаций. Это особенно полезно, если вы знаете примерный размер итоговой строки.

Сравнение производительности (грубо):

  • + в цикле: O(n²) по времени и памяти
  • push_str с with_capacity: O(n) — линейно

Ошибка №4: Использование format! там, где не нужно

Как это выглядит

let name = "Алексей";
let greeting = format!("{}", "Привет, " + name); // Избыточно

В чём суть ошибки

Макрос format! — это мощный инструмент, но он создаёт новую строку каждый раз. Если вам нужно просто склеить две строки, format! может быть медленнее, чем прямой вызов push_str или +. Кроме того, это делает код менее читаемым.

Как исправить

Используйте format! только когда вам нужно сложное форматирование (числа, выравнивание, несколько переменных). Для простой конкатенации двух строк:

let greeting = format!("Привет, {}!", name); // Хорошо
// Но если просто две строки:
let s1 = String::from("Hello");
let s2 = String::from("World");
let result = s1 + "                

Блоги

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