История вопроса
В Rust различают два основных строковых типа — &str (срез строки, неизменяемая, часто строковый литерал) и String (динамическая, изменяемая строка). На ранних этапах разработки языка выбор между ними позволил упростить работу с эффективной памятью и обеспечить безопасность типов при обработке текстовых данных благодаря строгой системе владения и ссылок.
Проблема
Многие разработчики путаются при взаимодействии между этими типами. К примеру, строковый литерал — это &'static str, то есть ссылка на неизменяемую строку, выделенную на этапе компиляции, в то время как String может динамически расширяться и содержать данные, полученные в рантайме. Возникают вопросы, как конвертировать между типами, правильно использовать владение и избегать лишних копирований.
Решение
Конвертация между &str и String прозрачна, если понимать основные правила владения:
String можно через ссылку (my_string.as_str()) или простое заимствование (&my_string).&str в String можно с помощью to_string() или String::from().Пример кода:
fn main() { let s_literal: &str = "hello"; let s_string: String = String::from(s_literal); let s_slice: &str = &s_string; let new_string = s_slice.to_string(); println!("{} {}", s_string, new_string); }
Ключевые особенности:
&str не занимает память в куче, всегда неизменяемый.String — выделяет память динамически, можно изменять.Можно ли модифицировать строковый литерал в Rust?
Нет, строковый литерал (&'static str) всегда неизменяем, любая попытка изменить символ приведет к ошибке на этапе компиляции.
Достаточно ли вызвать .to_string() на &str, чтобы получить изменяемую строку без лишних копирований?
Нет, .to_string() всегда выделяет новую память и копирует содержимое. Это неизбежно, если нужна изменяемая строка на основе среза.
Можно ли получить ссылку &str из String без риска утечки времени жизни?
Да, ссылка, полученная таким образом (let s: &str = &my_string;), живет не дольше исходного String. Попытка вернуть &str на локальный String из функции вызовет ошибку времени жизни.
&str на временный String, который выходит из области видимости&str в String без необходимости (лишние аллокации)String::from("text") не создаст копию данныхФункция возвращает &str, ссылающуюся на временный String внутри функции:
fn faulty() -> &str { let s = String::from("Oops"); &s // ошибка времени жизни! }
Плюсы:
Минусы:
Функция сразу возвращает String и передаёт владение вызывающему:
fn correct() -> String { String::from("Safe!") }
Плюсы:
Минусы: