为什么不鼓励接受对 String (&String)、Vec (&Vec) 或 Box (&Box) 的引用作为函数参数? [英] Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?
问题描述
我写了一些以 &String
作为参数的 Rust 代码:
I wrote some Rust code that takes a &String
as an argument:
fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}
我还编写了引用 Vec
或 Box
的代码:
I've also written code that takes in a reference to a Vec
or Box
:
fn total_price(prices: &Vec<i32>) -> i32 {
prices.iter().sum()
}
fn is_even(value: &Box<i32>) -> bool {
**value % 2 == 0
}
但是,我收到了一些反馈,说这样做不是一个好主意.为什么不呢?
However, I received some feedback that doing it like this isn't a good idea. Why not?
推荐答案
TL;DR:可以改用 &str
, &[T]
或 &T
以允许使用更通用的代码.
TL;DR: One can instead use &str
, &[T]
or &T
to allow for more generic code.
使用
String
或Vec
的主要原因之一是因为它们允许增加或减少容量.然而,当你接受一个不可变的引用时,你不能在Vec
或String
上使用任何这些有趣的方法.
One of the main reasons to use a
String
or aVec
is because they allow increasing or decreasing the capacity. However, when you accept an immutable reference, you cannot use any of those interesting methods on theVec
orString
.
接受 &String
、&Vec
或 &Box
还要求在调用函数之前要在堆上分配的参数.接受 &str
允许字符串文字(保存在程序数据中)并接受 &[T]
或 &T
允许堆栈分配的数组或变量.不必要的分配是一种性能损失.当您尝试在测试或 main
方法中调用这些方法时,通常会立即暴露:
Accepting a &String
, &Vec
or &Box
also requires the argument to be allocated on the heap before you can call the function. Accepting a &str
allows a string literal (saved in the program data) and accepting a &[T]
or &T
allows a stack-allocated array or variable. Unnecessary allocation is a performance loss. This is usually exposed right away when you try to call these methods in a test or a main
method:
awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
另一个性能考虑是 &String
、&Vec
和 &Box
引入了不必要的间接层,如您必须取消引用 &String
以获得 String
,然后执行第二次取消引用以结束 &str
.
Another performance consideration is that &String
, &Vec
and &Box
introduce an unnecessary layer of indirection as you have to dereference the &String
to get a String
and then perform a second dereference to end up at &str
.
相反,你应该接受一个 string slice (&str
),一个 slice (&[T]
),或者只是一个参考(&T
).&String
、&Vec
或 &Box
将被自动强制(通过 deref coercion) 到 &str
、&[T]
或 &T
,分别.
Instead, you should accept a string slice (&str
), a slice (&[T]
), or just a reference (&T
). A &String
, &Vec<T>
or &Box<T>
will be automatically coerced (via deref coercion) to a &str
, &[T]
or &T
, respectively.
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代码>.
Now you can call these methods with a broader set of types. For example, awesome_greeting
can be called with a string literal ("Anna"
) or an allocated String
. total_price
can be called with a reference to an array (&[1, 2, 3]
) or an allocated Vec
.
如果您想从 String
或 Vec
中添加或删除项目,您可以使用 可变引用 (&mut String
或 &mut Vec
):
If you'd like to add or remove items from the String
or Vec<T>
, you can take a mutable reference (&mut String
or &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);
}
特别是对于切片,您还可以接受 &mut [T]
或 &mut str
.这允许您改变切片内的特定值,但您不能更改切片内的项目数(这意味着它对字符串非常有限):
Specifically for slices, you can also accept a &mut [T]
or &mut str
. This allows you to mutate a specific value inside the slice, but you cannot change the number of items inside the slice (which means it's very restricted for strings):
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();
}
}
这篇关于为什么不鼓励接受对 String (&String)、Vec (&Vec) 或 Box (&Box) 的引用作为函数参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!