以借用类型为参数

说明

当你为函数选择参数类型时,使用带强制隐式转换的目标会增加你代码的复杂度。在这种情况下,函数将会接受更多的输入参数类型。

使用可切片类型或者胖指针类型没有限制。事实上,你应该总是用借用类型(borrowed type), 而不是自有数据类型的借用(borrowing the owned type)。 例如&str 而非 &String, &[T] 而非 &Vec<T>, 或者 &T 而非 &Box<T>.

当自有数据结构(owned type)的实例已经提供了一个访问数据的间接层时,使用借用类型可以让你避免增加间接层。举例来说,String类型有一层间接层,所以&String将有两个间接层。我们可以用&Str来避免这种情况,无论何时调用函数,强制&String转换为&Str

例子

在这个例子中,我们将说明使用&String&Str作为函数参数的区别。这个思路用于对比&Vec<T>&[T]&T&Box<T>也适用。

考虑一个我们想要确定一个单词是否包含3个连续的元音字母的例子。我们不需要获得字符串的所有权,所以我们将获取一个引用。

代码如下:

  1. fn three_vowels(word: &String) -> bool {
  2. let mut vowel_count = 0;
  3. for c in word.chars() {
  4. match c {
  5. 'a' | 'e' | 'i' | 'o' | 'u' => {
  6. vowel_count += 1;
  7. if vowel_count >= 3 {
  8. return true
  9. }
  10. }
  11. _ => vowel_count = 0
  12. }
  13. }
  14. false
  15. }
  16. fn main() {
  17. let ferris = "Ferris".to_string();
  18. let curious = "Curious".to_string();
  19. println!("{}: {}", ferris, three_vowels(&ferris));
  20. println!("{}: {}", curious, three_vowels(&curious));
  21. // 至此运行正常,但下面两行就会失败:
  22. // println!("Ferris: {}", three_vowels("Ferris"));
  23. // println!("Curious: {}", three_vowels("Curious"));
  24. }

这里能够正常运行是因为我们传的参数是&String类型。最后注释的两行运行失败是因为&str类型不能强制隐式转换为&String类型。我们靠修改参数类型即可轻松解决。

例如,如果我们把函数定义改为:

```rust, ignore fn three_vowels(word: &str) -> bool {

  1. 那么两种版本都能编译通过并打印相同的输出。
  2. ```bash
  3. Ferris: false
  4. Curious: true

等等,这并不是全部!这里还有点说道。你可能对自己说,这没啥事,我永远不会用&'static str当输入参数(像我们刚刚输入"Ferris"这种情况)。即使不考虑这个特殊例子,你还会发现使用&Str类型将会比&String类型带给你更大的灵活性。

让我们现在考虑一个例子:当给定一个句子,我们需确定句子中是否有单词包含3个连续的元音字母。我们也许应该用刚刚写好的函数来对句子中的每个单词做判断。 An example of this could look like this:

  1. fn three_vowels(word: &str) -> bool {
  2. let mut vowel_count = 0;
  3. for c in word.chars() {
  4. match c {
  5. 'a' | 'e' | 'i' | 'o' | 'u' => {
  6. vowel_count += 1;
  7. if vowel_count >= 3 {
  8. return true
  9. }
  10. }
  11. _ => vowel_count = 0
  12. }
  13. }
  14. false
  15. }
  16. fn main() {
  17. let sentence_string =
  18. "Once upon a time, there was a friendly curious crab named Ferris".to_string();
  19. for word in sentence_string.split(' ') {
  20. if three_vowels(word) {
  21. println!("{} has three consecutive vowels!", word);
  22. }
  23. }
  24. }

运行我们&Str参数函数定义版本会输出:

  1. curious has three consecutive vowels!

然而,使用&String版本的函数无法在这个例子中使用。这是因为字符串的切片是&Str类型而非&String类型,其转换为&String类型不是隐性的,然而&String转换为&Str是低开销且隐性的。

参阅