Historia pytania
W Rust rozróżnia się dwa podstawowe typy ciągów znaków — &str (slice ciągu, niemutowalny, często literał ciągu) i String (dynamiczny, mutowalny ciąg). Na wczesnych etapach rozwoju języka wybór między nimi umożliwił uproszczenie pracy z pamięcią wydajną i zapewnienie bezpieczeństwa typów przy przetwarzaniu danych tekstowych dzięki rygorystycznemu systemowi własności i odwołań.
Problem
Wielu programistów myli się przy interakcji między tymi typami. Na przykład, literał ciągu to &'static str, czyli odwołanie do niemutowalnego ciągu, przydzielonego na etapie kompilacji, podczas gdy String może dynamicznie się rozszerzać i zawierać dane uzyskane w czasie wykonywania. Pojawiają się pytania, jak konwertować między typami, prawidłowo używać własności i unikać niepotrzebnych kopiowań.
Rozwiązanie
Konwersja między &str a String jest przejrzysta, jeśli rozumieć podstawowe zasady własności:
String za pomocą odwołania (my_string.as_str()) lub prostego pożyczania (&my_string).&str w String można za pomocą to_string() lub String::from().Przykład kodu:
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); }
Kluczowe cechy:
&str nie zajmuje pamięci na stercie, zawsze niemutowalny.String — przydziela pamięć dynamicznie, można modyfikować.Czy można modyfikować literał ciągu w Rust?
Nie, literał ciągu (&'static str) zawsze jest niemutowalny, każda próba zmiany znaku spowoduje błąd na etapie kompilacji.
Czy wystarczy wywołać .to_string() na &str, aby uzyskać mutowalny ciąg bez niepotrzebnych kopiowań?
Nie, .to_string() zawsze przydziela nową pamięć i kopiuje zawartość. To nieuniknione, jeśli potrzeba mutowalnego ciągu na podstawie slice'a.
Czy można uzyskać odwołanie &str z String bez ryzyka wycieku czasu życia?
Tak, odwołanie uzyskane w ten sposób (let s: &str = &my_string;) żyje nie dłużej niż oryginalny String. Próba zwrócenia &str z lokalnego String z funkcji spowoduje błąd czasu życia.
&str na tymczasowym String, który wychodzi poza zakres&str na String bez potrzeby (niepotrzebne alokacje)String::from("text") nie stworzy kopii danychFunkcja zwraca &str, odwołujące się do tymczasowego String wewnątrz funkcji:
fn faulty() -> &str { let s = String::from("Oops"); &s // błąd czasu życia! }
Zalety:
Wady:
Funkcja od razu zwraca String i przekazuje własność do wywołującego:
fn correct() -> String { String::from("Safe!") }
Zalety:
Wady: