Historia pytania:
W porównaniu z językami natywnymi (C/C++), Rust buduje bezpieczną pracę z ciągami dzięki ścisłemu rozdzieleniu typów referencyjnych (&str) i własnościowych (String). To eliminuje większość błędów związanych z niewłaściwą pamięcią, przekroczeniem bufora i double-free.
Problem:
W przeciwieństwie do dorosłych języków z GC, gdzie każdy ciąg żyje w zarządzanej pamięci, w Rust trzeba jasno rozumieć, kto jest właścicielem ciągu, jak długo on żyje i jak uniknąć dangling reference po modyfikacjach. Praca z ciągami UTF-8 wymaga ostrożności przy indeksowaniu i zmianach.
Rozwiązanie:
W Rust String to mutowalny, przydzielany na heapie ciąg, który jest właścicielem swojej zawartości. &str to niemutowalna referencja do ciągu bajtów z gwarancją UTF-8. W razie potrzeby można bezpiecznie konwertować (&str -> String i odwrotnie) za pomocą metod std.
Przykład kodu:
fn main() { let owned: String = String::from("Rust"); let borrowed: &str = &owned; let primitive: &str = "Hello"; // literał zawsze jest &str // Konwersja &str -> String let s: String = primitive.to_string(); // Konwersja String -> &str let st: &str = &s; println!("{} {} {} {}", owned, borrowed, primitive, st); }
Kluczowe cechy:
&'static str, a nie StringDlaczego nie można indeksować ciągu jak s[1] lub s[i]?
Ciągi Rust są w UTF-8, dlatego indeksowanie nie jest dostępne bezpośrednio: s[i] nie zwraca i-tego znaku, a czasami prowadzi do panic przy dostępie do niewłaściwej granicy bajtów. Zamiast tego używaj metod .chars().nth(i) lub .get(start..end).
Czy można bezpiecznie modyfikować &str?
Nie można — &str jest zawsze niemutowalnym slice. Do modyfikacji używaj to_owned/to_string, lub korzystaj z String/Vec<u8>.
Czym zasadniczo różni się String::from("abc") od "abc".to_string()?
Te opcje są równoważne pod względem wyniku, obie tworzą String z kopiowaniem danych z &str. Różnica leży jedynie w stylu: na przykład to_string jest zrealizowane za pomocą trait ToString, podczas gdy String::from bardziej kontrastowo wyraża intencję "stwórz własność".
Funkcja przyjmowała String i robiła niepotrzebne kopiowanie ciągu wewnątrz (clone), a następnie pisała slice w inną funkcję, zapominając przedłużyć okres życia źródła. Wynik: dangling reference & crash.
Plusy:
Minusy:
Funkcja przyjmuje &str, a jeśli potrzebne są modyfikacje — wewnętrznie wywołuje .to_string(), na zewnątrz cała logika pozostaje "zero copy". Okresy życia pod kontrolą, żadnych zbędnych alokacji.
Plusy:
Minusy: