TL; DR: Można zamiast tego użyć &str
, &[T]
lub &T
w celu umożliwienia kod bardziej ogólny.
Jednym z głównych powodów używania a String
lub a Vec
jest to, że pozwalają one zwiększyć lub zmniejszyć pojemność. Jeśli jednak zaakceptujesz niezmienne odwołanie, nie możesz użyć żadnej z tych interesujących metod na platformie Vec
lub String
.
Przyjmowanie &String
, &Vec
czy &Box
też wymaga argumentu, która ma zostać przydzielona na stercie, zanim będzie można wywołać funkcję. Zaakceptowanie a &str
zezwala na literał łańcuchowy (zapisany w danych programu) i akceptuje &[T]
lub &T
zezwala na tablicę lub zmienną przydzieloną stosem. Niepotrzebna alokacja to utrata wydajności. Zwykle jest to widoczne od razu, gdy próbujesz wywołać te metody w teście lub main
metodzie:
awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
Inną kwestią związaną z wydajnością jest to &String
, &Vec
i &Box
wprowadź niepotrzebną warstwę pośrednią, ponieważ musisz wyłuskać, &String
aby uzyskać a, String
a następnie wykonać drugą dereferencję, aby zakończyć &str
.
Zamiast tego powinieneś zaakceptować kawałek ciągu ( &str
), kawałek ( &[T]
) lub po prostu odwołanie ( &T
). A &String
, &Vec<T>
lub &Box<T>
zostanie automatycznie wymuszona odpowiednio na a &str
, &[T]
lub &T
.
fn awesome_greeting(name: &str) {
println!("Wow, you are awesome, {}!", name);
}
fn total_price(prices: &[i32]) -> i32 {
prices.iter().sum()
}
fn is_even(value: &i32) -> bool {
*value % 2 == 0
}
Teraz możesz wywoływać te metody z szerszym zestawem typów. Na przykład awesome_greeting
można wywołać ciąg literal ( "Anna"
) lub zaalokować String
. total_price
można wywołać z odwołaniem do tablicy ( &[1, 2, 3]
) lub zaalokowaną Vec
.
Jeśli chcesz dodać lub usunąć elementy z String
lub Vec<T>
, możesz wziąć zmienne odwołanie ( &mut String
lub &mut Vec<T>
):
fn add_greeting_target(greeting: &mut String) {
greeting.push_str("world!");
}
fn add_candy_prices(prices: &mut Vec<i32>) {
prices.push(5);
prices.push(25);
}
W szczególności w przypadku plasterków możesz również zaakceptować &mut [T]
lub &mut str
. Pozwala to na zmianę określonej wartości wewnątrz plasterka, ale nie można zmienić liczby elementów wewnątrz plasterka (co oznacza, że jest to bardzo ograniczone do ciągów znaków):
fn reset_first_price(prices: &mut [i32]) {
prices[0] = 0;
}
fn lowercase_first_ascii_character(s: &mut str) {
if let Some(f) = s.get_mut(0..1) {
f.make_ascii_lowercase();
}
}
&str
jest bardziej ogólne (np. Nakłada mniej ograniczeń) bez ograniczonych możliwości”? Ponadto: myślę, że punkt 3 często nie jest aż tak ważny. ZazwyczajVec
s iString
s będą znajdować się na stosie, a często nawet w pobliżu bieżącej ramki stosu. Stos jest zwykle gorący, a dereferencja będzie obsługiwana z pamięci podręcznej procesora.total_price(&prices[0..4])
nie wymaga przydzielania nowego wektora dla wycinka.&str
i dlaczego (pochodzi z Pythona, więc zwykle nie zajmuję się bezpośrednio typami). Wszystko toOprócz odpowiedzi Shepmaster jest kolejnym powodem do zaakceptowania
&str
(i podobnie&[T]
etc) to z powodu wszystkich innych typów opróczString
i&str
że również spełniaćDeref<Target = str>
. Jednym z najbardziej godnych uwagi przykładów jest toCow<str>
, że pozwala bardzo elastycznie określić, czy masz do czynienia z danymi posiadanymi, czy pożyczonymi.Jeśli masz:
Ale musisz to nazwać
Cow<str>
, musisz to zrobić:Gdy zmienisz typ argumentu na
&str
, możesz używać goCow
płynnie, bez zbędnego przydziału, tak jak w przypadkuString
:Zaakceptowanie
&str
sprawia, że wywoływanie funkcji jest bardziej jednolite i wygodne, a „najłatwiejszy” sposób jest teraz również najbardziej efektywny. Te przykłady będą również działać zCow<[T]>
itp.źródło