tipos - ¿Por qué se desaconseja aceptar una referencia a un String(& String), Vec(& Vec) o Box(& Box) como argumento de función?
sub con parametros vba excel (2)
Además de
la respuesta de Shepmaster
, otra razón para aceptar a
&str
(y de manera similar
&[T]
etc.) se debe a todos los otros tipos,
además de
String
y
&str
que también satisfacen a
Deref<Target = str>
.
Uno de los ejemplos más notables es
Cow<str>
, que le permite ser muy flexible acerca de si se trata de datos propios o prestados.
Si usted tiene:
fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}
Pero debes llamarlo con una
Cow<str>
, tendrás que hacer esto:
let c: Cow<str> = Cow::from("hello");
// Allocate an owned String from a str reference and then makes a reference to it anyway!
awesome_greeting(&c.to_string());
Cuando cambia el tipo de argumento a
&str
, puede usar
Cow
sin problemas, sin ninguna asignación innecesaria, al igual que con
String
:
let c: Cow<str> = Cow::from("hello");
// Just pass the same reference along
awesome_greeting(&c);
let c: Cow<str> = Cow::from(String::from("hello"));
// Pass a reference to the owned string that you already have
awesome_greeting(&c);
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 con
Cow<[T]>
etc.
Escribí un código Rust que toma un
&String
como argumento:
fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}
También he escrito código que toma una referencia a un
Vec
o
Box
:
fn total_price(prices: &Vec<i32>) -> i32 {
prices.iter().sum()
}
fn is_even(value: &Box<i32>) -> bool {
**value % 2 == 0
}
Sin embargo, recibí algunos comentarios de que hacerlo así no es una buena idea. Por qué no?
TL; DR: en su lugar, se puede usar
&str
,
&[T]
o
&T
para permitir un código más genérico.
-
Una de las razones principales para usar una
String
o unaVec
es porque permiten aumentar o disminuir la capacidad. Sin embargo, cuando acepta una referencia inmutable, no puede usar ninguno de esos métodos interesantes enVec
oString
. -
Aceptar una
&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 a&str
permite un literal de cadena (guardado en los datos del programa) y aceptar a&[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 métodomain
:awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
-
Otra consideración de rendimiento es que
&String
,&Vec
y&Box
introducen una capa innecesaria de indirección, ya que debe desreferenciar&String
para obtener unaString
y luego realizar una segunda desreferencia para terminar en&str
.
En su lugar, debe aceptar un
segmento de cadena
(
&str
), un
segmento
(
&[T]
) o simplemente una referencia (
&T
).
A
&String
,
&Vec<T>
o
&Box<T>
se convertirán automáticamente en 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 un literal de cadena (
"Anna"
)
o
una
String
asignada.
total_price
puede llamar a
total_price
con una referencia a una matriz (
&[1, 2, 3]
)
o
un
Vec
asignado.
Si desea agregar o eliminar elementos de
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 a
&mut [T]
o
&mut str
.
Esto le permite mutar un valor específico dentro del segmento, pero no puede cambiar la cantidad de elementos dentro del segmento (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();
}
}