TL; DR: En su lugar &str
, se puede usar &[T]
o &T
permitir un código más genérico.
Una de las principales razones para utilizar a String
o a Vec
es porque permiten aumentar o disminuir la capacidad. Sin embargo, cuando acepta una referencia inmutable, no puede utilizar ninguno de esos métodos interesantes en Vec
o String
.
Aceptar un &String
, &Vec
o &Box
también requiere que el argumento se asigne en el montón antes de poder llamar a la función. Aceptar un &str
permite un literal de cadena (guardado en los datos del programa) y aceptar un &[T]
o &T
permite una matriz o variable asignada a la pila. La asignación innecesaria es una pérdida de rendimiento. Esto generalmente se expone de inmediato cuando intenta llamar a estos métodos en una prueba o un main
método:
awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
Otra consideración de rendimiento es eso &String
, &Vec
e &Box
introducir una capa innecesaria de indirección, ya que debe eliminar la referencia &String
para obtener una String
y luego realizar una segunda desreferencia para terminar en &str
.
En su lugar, debe aceptar una cadena de segmento ( &str
), un segmento ( &[T]
) o simplemente una referencia ( &T
). A &String
, &Vec<T>
o &Box<T>
será automáticamente coaccionado a a &str
, &[T]
o &T
, respectivamente.
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
}
Ahora puede llamar a estos métodos con un conjunto más amplio de tipos. Por ejemplo, awesome_greeting
se puede llamar con una cadena literal ( "Anna"
) o un archivo String
. total_price
se puede llamar con una referencia a una matriz ( &[1, 2, 3]
) o un archivo Vec
.
Si desea agregar o eliminar elementos del String
o Vec<T>
, puede tomar una referencia mutable ( &mut String
o &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);
}
Específicamente para sectores, también puede aceptar &mut [T]
o &mut str
. Esto le permite mutar un valor específico dentro del sector, pero no puede cambiar la cantidad de elementos dentro del sector (lo que significa que está muy restringido para las cadenas):
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
es más general (como en: impone menos restricciones) sin capacidades reducidas"? Además, creo que el punto 3 no suele ser tan importante. Por lo general,Vec
syString
s vivirán en la pila y, a menudo, incluso cerca del marco de pila actual. La pila suele estar caliente y la desreferenciación se realizará desde una memoria caché de la CPU.total_price(&prices[0..4])
no requiere la asignación de un nuevo vector para el segmento.&str
y por eso (que viene de Python, así que por lo general no se ocupan explícitamente de tipos). Aclaró todo eso perfectamenteAdemás de la respuesta de Shepmaster , otra razón para aceptar a
&str
(y de manera similar,&[T]
etc.) es porque todos los otros tipos ademásString
y&str
eso también satisfacenDeref<Target = str>
. Uno de los ejemplos más notables es elCow<str>
que le permite ser muy flexible sobre si se trata de datos propios o prestados.Si usted tiene:
Pero debes llamarlo con a
Cow<str>
, tendrás que hacer esto:Cuando cambia el tipo de argumento a
&str
, puede usarloCow
sin problemas, sin ninguna asignación innecesaria, al igual que conString
:Aceptar
&str
hace que llamar a su función sea más uniforme y conveniente, y la forma "más fácil" ahora también es la más eficiente. Estos ejemplos también funcionarán conCow<[T]>
etc.fuente