我写了一些以 a&String作为参数的 Rust 代码:
&String
fn awesome_greeting(name: &String) { println!("Wow, you are awesome, {}!", name); }
我还编写了引用 a Vecor的代码Box:
Vec
Box
fn total_price(prices: &Vec<i32>) -> i32 { prices.iter().sum() } fn is_even(value: &Box<i32>) -> bool { **value % 2 == 0 }
但是,我收到了一些反馈,说这样做不是一个好主意。为什么不?
TL;DR:可以改为使用&str,&[T]或&T允许更通用的代码。
&str
&[T]
&T
String使用 a或 a的主要原因之一Vec是因为它们允许增加或减少容量。但是,当您接受不可变引用时,您不能在Vecor上使用任何这些有趣的方法String。
String
接受&String, &Vecor&Box还 需要 在调用函数之前在堆上分配参数。接受 a&str允许字符串文字(保存在程序数据中)并接受 a &[T]or&T允许堆栈分配的数组或变量。不必要的分配是性能损失。当您尝试在测试或方法中调用这些方法时,这通常会立即暴露main:
&Vec
&Box
main
awesome_greeting(&String::from("Anna")); total_price(&vec![42, 13, 1337]) is_even(&Box::new(42))
另一个性能考虑因素是&String,&Vec并&Box引入了不必要的间接层,因为您必须取消引用&String以获取 aString然后执行第二次取消引用以结束 at &str。
相反,您应该接受 字符串切片 ( &str)、 切片 ( &[T]) 或只是一个引用 ( &T)。A &String, &Vec<T>or&Box<T>将被自动强制(通过 deref coercion )分别为 a &str, &[T]or &T。
&Vec<T>
&Box<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 }
现在,您可以使用更广泛的类型集来调用这些方法。例如,awesome_greeting可以使用字符串文字 ( "Anna") 或 分配的String. total_price可以通过对数组 ( &[1, 2, 3]) 或 已分配的引用来调用Vec。
awesome_greeting
"Anna"
total_price
&[1, 2, 3]
如果您想在 or 中添加或删除项目String,Vec<T>您可以使用 可变引用 ( &mut Stringor &mut Vec<T>):
Vec<T>
&mut String
&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); }
特别是对于切片,您还可以接受 a&mut [T]或&mut str。这允许您改变切片内的特定值,但您不能更改切片内的项目数(这意味着它对字符串非常有限):
&mut [T]
&mut str
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(); } }