我写了一些Rust代码,它将&String
作为参数:
fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}
我还编写了一段代码,其中包含对Vec
或Box
的引用:
fn total_price(prices: &Vec<i32>) -> i32 {
prices.iter().sum()
}
fn is_even(value: &Box<i32>) -> bool {
**value % 2 == 0
}
然而,我收到一些反馈,这样做不是一个好主意。为什么不呢?
3条答案
按热度按时间zvms9eto1#
TL;DR:可以使用
&str
、&[T]
或&T
来允许更通用的代码。1.使用
String
或Vec
的一个主要原因是它们允许增加或减少容量。然而,当你接受一个不可变的引用时,你就不能在Vec
或String
上使用这些有趣的方法。1.接受
&String
、&Vec
或&Box
还要求在调用函数之前在堆上分配参数。接受&str
允许字符串文字(保存在程序数据中)并接受&[T]
或&T
时,允许堆栈分配数组或变量。不必要的分配会导致性能损失。当您尝试在测试或main
方法中调用这些方法时,通常会立即公开这些方法:第一个
1.另一个性能考虑因素是
&String
、&Vec
和&Box
引入了不必要的间接层,因为您必须取消引用&String
以获得String
,然后执行第二次取消引用以在&str
处结束。相反,您应该接受 * 字符串切片 (
&str
)、 切片 *(&[T]
)或仅接受引用(&T
)。&String
、&Vec<T>
或&Box<T>
将分别自动强制(通过 deref强制 )为&str
、&[T]
或&T
。第一个
现在,您可以使用更广泛的类型集来调用这些方法。例如,可以使用字符串文字(
"Anna"
) 或 * 分配的String
来调用awesome_greeting
。可以使用对数组(&[1, 2, 3]
) 或 * 分配的Vec
的引用来调用total_price
。如果您想在
String
或Vec<T>
中添加或删除项,可以使用 * 可变引用 *(&mut String
或&mut Vec<T>
):第一个
特别是对于切片,你也可以接受
&mut [T]
或&mut str
,这允许你在切片中改变一个特定的值,但是你不能改变切片中的项数(这意味着对于字符串来说,它是非常受限的):q8l4jmvw2#
除了Shepmaster's answer之外,接受
&str
(以及类似的&[T]
等)的另一个原因是,除了 *String
和&str
之外,所有其他类型 * 也满足Deref<Target = str>
。最值得注意的例子之一是Cow<str>
,它允许您非常灵活地处理自有数据还是借用数据。如果您有:
但是你需要用
Cow<str>
调用它,你必须这样做:当您将参数类型更改为
&str
时,您可以无缝地使用Cow
,而无需任何不必要的分配,就像使用String
一样:接受
&str
使得调用函数更加统一和方便,“最简单”的方法现在也是最有效的。这些例子也适用于Cow<[T]>
等。t0ybt7op3#
建议使用
&str
而不是&String
,因为&str
还满足&String
,它可以用于拥有的字符串和字符串切片,但不能用于拥有的字符串和字符串切片:使用向量来操作文本从来都不是一个好主意,甚至不值得讨论,因为你会失去所有的健全性检查和性能优化。字符串类型无论如何都在内部使用向量。记住,Rust使用UTF-8来存储字符串以提高存储效率。如果你使用向量,你必须重复所有的艰苦工作。除此之外,借用向量或装箱值应该是可以的。