TL; DR: On peut utiliser à la place &str
, &[T]
ou &T
pour permettre un code plus générique.
L'une des principales raisons d'utiliser a String
ou a Vec
est qu'ils permettent d'augmenter ou de diminuer la capacité. Cependant, lorsque vous acceptez une référence immuable, vous ne pouvez utiliser aucune de ces méthodes intéressantes sur le Vec
fichier ou String
.
L'acceptation d'un &String
, &Vec
ou nécessite&Box
également que l'argument soit alloué sur le tas avant de pouvoir appeler la fonction. L'acceptation de a &str
autorise un littéral de chaîne (enregistré dans les données du programme) et l'acceptation de &[T]
ou &T
autorise un tableau ou une variable alloué à la pile. Une allocation inutile est une perte de performance. Ceci est généralement exposé immédiatement lorsque vous essayez d'appeler ces méthodes dans un test ou une main
méthode:
awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
Une autre considération pour les performances est que &String
, &Vec
et &Box
introduisez une couche d'indirection inutile car vous devez déréférencer le &String
pour obtenir un String
, puis effectuer une deuxième déréférence pour aboutir à &str
.
Au lieu de cela, vous devriez accepter une chaîne slice ( &str
), une slice ( &[T]
) ou simplement une référence ( &T
). A &String
, &Vec<T>
ou &Box<T>
sera automatiquement forcé à a &str
, &[T]
ou &T
, respectivement.
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
}
Vous pouvez désormais appeler ces méthodes avec un ensemble plus large de types. Par exemple, awesome_greeting
peut être appelé avec une chaîne littérale ( "Anna"
) ou allouée String
. total_price
peut être appelé avec une référence à un tableau ( &[1, 2, 3]
) ou un fichier alloué Vec
.
Si vous souhaitez ajouter ou supprimer des éléments du String
ou Vec<T>
, vous pouvez prendre une référence mutable ( &mut String
ou &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);
}
Spécifiquement pour les tranches, vous pouvez également accepter un &mut [T]
ou &mut str
. Cela vous permet de muter une valeur spécifique à l'intérieur de la tranche, mais vous ne pouvez pas modifier le nombre d'éléments à l'intérieur de la tranche (ce qui signifie qu'elle est très limitée pour les chaînes):
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
est plus général (comme dans: impose moins de restrictions) sans capacités réduites"? Aussi: le point 3 n'est souvent pas si important que je pense. Habituellement,Vec
s etString
s vivront sur la pile et souvent même quelque part près du cadre de pile actuel. La pile est généralement chaude et le déréférencement sera servi à partir d'un cache CPU.total_price(&prices[0..4])
ne nécessite pas d'allouer un nouveau vecteur pour la tranche.&str
et pourquoi (venant de Python, donc je ne traite généralement pas explicitement des types). Tout cela a été parfaitementEn plus de la réponse de Shepmaster , une autre raison d'accepter un
&str
(et de même ,&[T]
etc.) est à cause de tous les autres types d' ailleursString
et&str
qui satisfont aussiDeref<Target = str>
. L'un des exemples les plus notables estCow<str>
qui vous permet d'être très flexible quant à savoir si vous traitez des données détenues ou empruntées.Si tu as:
Mais vous devez l'appeler avec un
Cow<str>
, vous devrez le faire:Lorsque vous modifiez le type d'argument en
&str
, vous pouvez l'utiliser deCow
manière transparente, sans allocation inutile, tout comme avecString
:Accepter
&str
rend l'appel de votre fonction plus uniforme et plus pratique, et le moyen le plus "simple" est désormais aussi le plus efficace. Ces exemples fonctionneront également avecCow<[T]>
etc.la source