我写了一些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
}
然而,我收到一些反馈说这样做不是一个好主意。为什么不呢?
4条答案
按热度按时间fnvucqvd1#
&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
,这允许你在切片内改变一个特定的值,但是你不能改变切片内的元素数量(这意味着它对字符串的限制非常严格):t30tvxxf2#
除了Shepmaster's answer之外,接受
&str
(以及类似的&[T]
等)的另一个原因是,除了 *String
和&str
之外,所有其他类型 * 也满足Deref<Target = str>
,其中一个最值得注意的例子是Cow<str>
,它允许您非常灵活地处理拥有的数据还是借用的数据。如果您有:
但是你需要用一个
Cow<str>
来调用它,你必须这样做:当您将参数类型更改为
&str
时,可以无缝地使用Cow
,而无需任何不必要的分配,就像使用String
一样:接受
&str
使得调用你的函数更加统一和方便,并且现在“最简单”的方法也是最有效的。这些例子也适用于Cow<[T]>
等。tf7tbtn23#
建议使用
&str
而不是&String
,因为&str
也满足&String
,它可以用于拥有的字符串和字符串切片,但不能用于拥有的字符串和字符串切片:使用向量来操作文本从来都不是一个好主意,甚至不值得讨论,因为你会失去所有的健全性检查和性能优化。字符串类型内部使用向量无论如何。记住,Rust使用UTF-8字符串的存储效率。如果你使用向量,你必须重复所有的艰苦工作。除此之外,借用向量或装箱的值应该是可以的。
v1uwarro4#
因为这些类型可以被强制,所以如果我们使用这些类型,函数将接受更少的类型:
1-可以将对String的引用强制到str切片。例如,创建一个函数:
如果您通过
&str
,则不会被接受:但如果该函数接受
&str
我们可以将两种类型都传递给函数
这样,我们的函数就可以接受更多类型
2-类似地,对Box
&Box[T]
的引用将被强制为对BoxBox[&T]
内的值的引用。这只接受
&Box<&str>
类型如果将
&str
作为类型传递,则可以同时传递两种类型3-对Vec的引用和对数组的引用之间存在类似的关系
这对两种类型都有效