Правильный способ вернуть новую строку из функции в Rust
Основная проблема: владение и время жизни
Ошибка, которую вы получили, — классическая для новичков в Rust. Она связана с тем, что result — это локальная переменная, которая живёт только внутри функции. Когда функция завершается, память, выделенная под result, освобождается. Возвращать ссылку на уже удалённые данные нельзя — это приводит к висячим указателям, которых Rust не допускает.
Правильное решение: возвращайте String, а не &str
Самый идиоматичный способ — вернуть String. В Rust строка String владеет своими данными, поэтому её можно безопасно передать из функции наружу. Вызывающий код получит право собственности на эту строку.
fn add_prefix(s: &str) -> String {
let result = String::from("prefix_") + s;
result // или просто return result;
}
fn main() {
let original = "hello";
let new_string = add_prefix(original);
println!("{}", new_string); // "prefix_hello"
// new_string теперь владеет данными, мы можем использовать её дальше
}
Упрощённый вариант без промежуточной переменной
Часто в Rust пишут короче, используя макрос format! или цепочку методов:
fn add_prefix(s: &str) -> String {
format!("prefix_{}", s)
}
// Или через to_string() и конкатенацию:
fn add_prefix_v2(s: &str) -> String {
"prefix_".to_string() + s
}
Почему это правильно?
- Владение: Функция создаёт новую строку, владеет ею и передаёт владение вызывающему коду. Никаких ссылок на локальные данные.
- Производительность:
Stringхранит данные в куче, но возврат через перемещение — это дешёвая операция (копируется только указатель, длина и ёмкость). - Гибкость: Вызывающий код может решить, что делать со строкой: взять срез (&str), модифицировать, передать дальше и т.д.
Когда всё же возвращать &str?
Ссылку &str можно возвращать только в двух случаях:
- Если строка является статической (
&'static str) — например, строковый литерал. - Если строка является частью данных, которыми владеет кто-то другой (например, поле структуры, полученное через аргумент).
Пример с &'static str:
fn get_greeting() -> &'static str {
"Hello, world!" // живёт всю программу
}
Дополнительный совет: используйте String как тип возврата
В Rust принято возвращать String из функций, которые создают новые строки. Не бойтесь, что это приводит к лишним аллокациям — компилятор часто оптимизирует такие случаи. Если вы заметите проблемы с производительностью, можно будет рассмотреть альтернативы (например, передачу &mut String в аргументе), но для 99% случаев возврат String — это правильный и чистый подход.
Альтернативный подход: передача изменяемой ссылки на буфер
Если вы пишете высокопроизводительный код и хотите избежать лишних аллокаций, можно передавать в функцию изменяемую ссылку на String и дописывать данные в неё. Это полезно, когда вы многократно вызываете функцию в цикле.
fn add_prefix_into(s: &str, buffer: &mut String) {
buffer.push_str("prefix_");
buffer.push_str(s);
}
fn main() {
let mut result = String::new();
add_prefix_into("hello", &mut result);
println!("{}", result); // "prefix_hello"
}
Когда это оправдано?
- В горячих циклах, где каждая аллокация на счету.
- Когда нужно переиспользовать один и тот же буфер (например, в парсерах или сериализаторах).
Недостатки
- Менее идиоматично: Rust поощряет возврат значений, а не мутацию аргументов.
- Усложняет интерфейс: вызывающий код должен создать и поддерживать буфер.
Для большинства задач возврат String — это правильный выбор. Используйте мутацию буфера только после профилирования, когда вы точно знаете, что это узкое место.
Ответов пока нет.
Войдите чтобы оставить ответ