Dlaczego Rust ma Stringi str? Jakie są różnice między Stringi str? Kiedy używa się Stringzamiast stri odwrotnie? Czy jeden z nich staje się przestarzały?
Dlaczego Rust ma Stringi str? Jakie są różnice między Stringi str? Kiedy używa się Stringzamiast stri odwrotnie? Czy jeden z nich staje się przestarzały?
Odpowiedzi:
Stringjest dynamicznym typem łańcucha stosu, takim jak Vec: użyj go, gdy chcesz posiadać lub zmodyfikować dane łańcucha.
strto niezmienna 1 sekwencja bajtów UTF-8 o dynamicznej długości gdzieś w pamięci. Ponieważ rozmiar jest nieznany, można go obsługiwać tylko za wskaźnikiem. Oznacza to, że strnajczęściej 2 pojawia się jako &str: odniesienie do niektórych danych UTF-8, zwykle nazywanych „wycinkiem łańcucha” lub po prostu „wycinkiem”. Wycinek to tylko widok niektórych danych, które mogą znajdować się w dowolnym miejscu, np
"foo"to &'static str. Dane są zakodowane na stałe w pliku wykonywalnym i ładowane do pamięci podczas działania programu.String : Stringdereferences do &strwidoku z Stringdanych „s.Na stosie : np. Następujące tworzy tablicę bajtów przydzieloną do stosu, a następnie wyświetla te dane jako&str :
use std::str;
let x: &[u8] = &[b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(x).unwrap();
Podsumowując, użyj, Stringjeśli potrzebujesz posiadanych danych ciągów (takich jak przekazywanie ciągów do innych wątków lub budowanie ich w czasie wykonywania), i użyj, &strjeśli potrzebujesz tylko widoku ciągu.
Jest to identyczne z relacją między wektorem Vec<T>a wycinkiem &[T]i jest podobne do relacji między wartością Ta odniesieniem &Tdla typów ogólnych.
1 A strma ustaloną długość; nie można pisać bajtów poza końcem ani pozostawiać końcowych niepoprawnych bajtów. Ponieważ UTF-8 jest kodowaniem o zmiennej szerokości, to skutecznie zmusza wszystkie strs do niezmienności w wielu przypadkach. Ogólnie rzecz biorąc, mutacja wymaga zapisania większej lub mniejszej liczby bajtów niż wcześniej (np. Zastąpienie a(1 bajtu) przez ä(2+ bajty) wymagałoby więcej miejsca w str). Istnieją określone metody, które mogą modyfikować &strmiejsce, głównie te, które obsługują tylko znaki ASCII, takie jak make_ascii_uppercase.
2 Typy o dynamicznym rozmiarze pozwalają np. Rc<str>Na sekwencję liczonych referencji bajtów UTF-8 od wersji Rust 1.2. Rdza 1.21 pozwala łatwo tworzyć te typy.
[u8; N].
Rc<str>i Arc<str>można je teraz używać za pomocą standardowej biblioteki.
Mam C ++ tła i uważam, że to bardzo przydatna zastanowić Stringi &strpod względem c ++:
Stringjest jak std::string; jest właścicielem pamięci i wykonuje brudną robotę zarządzania pamięcią.&strjest jak char*(ale trochę bardziej wyrafinowana); wskazuje nam początek fragmentu w taki sam sposób, w jaki można uzyskać wskaźnik do zawartości std::string.Czy któreś z nich zniknie? Nie sądzę. Służą one dwóm celom:
Stringzachowuje bufor i jest bardzo praktyczny w użyciu. &strjest lekki i powinien być używany do „przeglądania” ciągów znaków. Możesz wyszukiwać, dzielić, analizować, a nawet zamieniać porcje bez konieczności przydzielania nowej pamięci.
&strmoże zajrzeć do środka, Stringponieważ może wskazywać na dosłowny ciąg znaków. Poniższy kod musi skopiować literały ciąg do Stringpamięci zarządzanej:
let a: String = "hello rust".into();
Poniższy kod pozwala używać samego literału bez kopiowania (tylko do odczytu)
let a: &str = "hello rust";
str, używany tylko jako &str, jest ciągiem znaków, odniesieniem do tablicy bajtów UTF-8.
Stringto ~strdawna, rosnąca, własna tablica bajtów UTF-8.
~strteraz byłoBox<str>
~strbył uprawiany, podczas gdy Box<str>nie był uprawiany. (To ~stri ~[T]były magicznie uprawiane, w przeciwieństwie do innych ~obiektów, było dokładnie tego powodem Stringi Vec<T>zostały wprowadzone, aby wszystkie zasady były proste i spójne.)
W rzeczywistości są zupełnie inne. Po pierwsze, a strjest niczym innym, jak tylko poziomem czcionki; można to uzasadnić tylko na poziomie typu, ponieważ jest to tak zwany typ dynamicznie wielkości (DST). Rozmiar, który strzajmuje, nie może być znany w czasie kompilacji i zależy od informacji o środowisku wykonawczym - nie można go zapisać w zmiennej, ponieważ kompilator musi wiedzieć w czasie kompilacji, jaki jest rozmiar każdej zmiennej. A strjest koncepcyjnie tylko rzędem u8bajtów z gwarancją, że tworzy poprawny UTF-8. Jak duży jest rząd? Nikt nie wie do czasu uruchomienia, dlatego nie można go zapisać w zmiennej.
Interesującą rzeczą jest to, że &stralbo każdy inny wskaźnik do strLike Box<str> nie istnieje w czasie wykonywania. Jest to tak zwany „wskaźnik tłuszczu”; jest wskaźnikiem z dodatkowymi informacjami (w tym przypadku wielkości rzeczy, na którą wskazuje), więc jest dwa razy większy. W rzeczywistości a &strjest dość zbliżone do String(ale nie do a &String). A &strto dwa słowa; jeden wskaźnik do pierwszego bajtu strai druga liczba, która opisuje, ile bajtów ma długość str.
W przeciwieństwie do tego, co powiedziano, a strnie musi być niezmienne. Jeśli możesz uzyskać wskaźnik &mut strjako wyłączny str, możesz go zmutować, a wszystkie bezpieczne funkcje, które go mutują, gwarantują utrzymanie ograniczenia UTF-8, ponieważ jeśli zostanie ono naruszone, wówczas nie zdefiniujemy zachowania, ponieważ biblioteka zakłada, że to ograniczenie jest prawda i nie sprawdza jej.
Co to jest String? To trzy słowa; dwa są takie same jak dla, &strale dodaje trzecie słowo, które jest pojemnością strbufora na stercie, zawsze na stercie (a strniekoniecznie jest na stercie), którą zarządza zanim zostanie wypełniona i będzie musiała ponownie przydzielić. Stringzasadzie posiadastr jak mówią; kontroluje to i może zmieniać jego rozmiar oraz ponownie przydzielać, kiedy uzna to za stosowne. Tak więc, Stringjak powiedziano, bliżej &strniż do str.
Inną rzeczą jest Box<str>; posiada również a, stra jego środowisko wykonawcze jest takie samo jak a, &strale jest także właścicielem w strprzeciwieństwie do, &strale nie może zmienić jego rozmiaru, ponieważ nie zna swojej pojemności, więc w zasadzie Box<str>można postrzegać jako stałą długość String, której nie można zmienić ( zawsze zamień go na Stringjeśli chcesz zmienić jego rozmiar).
Istnieje bardzo podobny związek między [T]i Vec<T>chyba nie ma ograniczenia UTF-8 i może posiadać dowolny typ, którego rozmiar nie jest dynamiczny.
Użycie strna poziomie typu służy głównie do tworzenia ogólnych abstrakcji &str; istnieje na poziomie typu, aby móc wygodnie pisać cechy. Teoretycznie strjako typ rzecz nie musiała istnieć i tylko to, &strale oznaczałoby to, że trzeba napisać dużo dodatkowego kodu, który może być teraz ogólny.
&strjest bardzo przydatny, aby móc mieć wiele różnych podciągów Stringbez konieczności kopiowania; jak powiedział String posiadastr na stercie zarządza, i jeśli można utworzyć tylko podciąg od a Stringz nowym Stringmusiałby skopiowana, ponieważ wszystko w Rust może mieć tylko jeden pojedynczy właściciela do czynienia z bezpieczeństwem pamięci. Na przykład możesz pokroić ciąg:
let string: String = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];
Mamy dwa różne podciągi strtego samego łańcucha. stringjest właścicielem rzeczywistego pełnego strbuforu na stercie, a &strpodłańcuchy są po prostu wskaźnikami tłuszczu do tego buforu na stercie.
std::Stringjest po prostu wektorem u8. Można znaleźć jego definicję w kodzie źródłowym . Jest alokowany na stos i można go uprawiać.
#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
vec: Vec<u8>,
}
strjest prymitywnym typem, zwanym także wycinkiem łańcucha . Wycinek łańcucha ma ustalony rozmiar. Dosłowny ciąg typu let test = "hello world"ma &'static strtyp. testjest odniesieniem do tego statycznie przydzielonego ciągu.
&strnie można na przykład modyfikować
let mut word = "hello world";
word[0] = 's';
word.push('\n');
strma zmienny plasterek &mut str, na przykład:
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)
let mut s = "Per Martin-Löf".to_string();
{
let (first, last) = s.split_at_mut(3);
first.make_ascii_uppercase();
assert_eq!("PER", first);
assert_eq!(" Martin-Löf", last);
}
assert_eq!("PER Martin-Löf", s);
Ale niewielka zmiana w UTF-8 może zmienić jego długość bajtu, a plasterek nie może ponownie przydzielić swojego odniesienia.
Krótko mówiąc, Stringtyp danych jest przechowywany na stercie (podobnie jak Vec) i masz dostęp do tej lokalizacji.
&strjest rodzajem plastra. Oznacza to, że jest to tylko odniesienie do już istniejącego Stringgdzieś na stosie.
&strnie dokonuje żadnej alokacji w czasie wykonywania. Tak więc ze względu na pamięć możesz użyć &strponad String. Pamiętaj jednak, że podczas używania &strmożesz mieć do czynienia z jawnym życiem.
strjest viewjuż obecne Stringw kupie.
Dla osób C # i Java:
String===StringBuilder &str Ciąg Rust === (niezmienny)Lubię myśleć o &strwidoku jako o łańcuchu, jak o internowanym łańcuchu w Javie / C #, w którym nie można go zmienić, wystarczy utworzyć nowy.
Oto szybkie i łatwe wyjaśnienie.
String- Rosnąca, dostępna struktura danych przydzielana do sterty. Można go przymusić do &str.
str- jest (teraz, w miarę rozwoju Rust) zmiennym ciągiem o stałej długości, który żyje na stercie lub w pliku binarnym. Możesz wchodzić w interakcje tylko z strpożyczonym typem za pomocą widoku wycinka ciągu, takiego jak &str.
Uwagi dotyczące użytkowania:
Preferuj, Stringjeśli chcesz posiadać lub mutować ciąg - na przykład przekazując ciąg do innego wątku itp.
Preferuj, &strjeśli chcesz mieć ciąg tylko do odczytu.
&strskłada się z dwóch komponentów: wskaźnik do niektórych bajtów, a długość”.