188

我写了一些以 a&String作为参数的 Rust 代码:

fn awesome_greeting(name: &String) {
    println!("Wow, you are awesome, {}!", name);
}

我还编写了引用 a Vecor的代码Box

fn total_price(prices: &Vec<i32>) -> i32 {
    prices.iter().sum()
}

fn is_even(value: &Box<i32>) -> bool {
    **value % 2 == 0
}

但是,我收到了一些反馈,说这样做不是一个好主意。为什么不?

4

2 回答 2

245

TL;DR:可以改为使用&str,&[T]&T允许更通用的代码。


  1. String使用 a或 a的主要原因之一Vec是因为它们允许增加或减少容量。但是,当您接受不可变引用时,您不能在Vecor上使用任何这些有趣的方法String

  2. 接受&String, &Vecor&Box需要在调用函数之前在堆上分配参数。接受 a&str允许字符串文字(保存在程序数据中)并接受 a &[T]or&T允许堆栈分配的数组或变量。不必要的分配是性能损失。当您尝试在测试或方法中调用这些方法时,这通常会立即暴露main

    awesome_greeting(&String::from("Anna"));
    
    total_price(&vec![42, 13, 1337])
    
    is_even(&Box::new(42))
    
  3. 另一个性能考虑因素是&String&Vec&Box引入了不必要的间接层,因为您必须取消引用&String以获取 aString然后执行第二次取消引用以结束 at &str

相反,您应该接受字符串切片( &str)、切片( &[T]) 或只是一个引用 ( &T)。A &String, &Vec<T>or&Box<T>将被自动强制(通过deref coercion)分别为 a &str, &[T]or &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


如果您想在 or 中添加或删除项目StringVec<T>您可以使用可变引用( &mut Stringor &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。这允许您改变切片内的特定值,但您不能更改切片内的项目数(这意味着它对字符串非常有限):

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();
    }
}
于 2016-10-12T18:50:50.360 回答
38

除了Shepmaster 的回答之外,接受 a &str(以及类似&[T]等)的另一个原因是因为所有其他类型除了 String并且&str也满足Deref<Target = str>. 最显着的例子之一是Cow<str>,它可以让您非常灵活地处理自己的数据还是借用的数据。

如果你有:

fn awesome_greeting(name: &String) {
    println!("Wow, you are awesome, {}!", name);
}

但是你需要用 a 来调用它Cow<str>,你必须这样做:

let c: Cow<str> = Cow::from("hello");
// Allocate an owned String from a str reference and then makes a reference to it anyway!
awesome_greeting(&c.to_string());

当您将参数类型更改为 时&str,您可以Cow无缝使用,无需任何不必要的分配,就像使用String

let c: Cow<str> = Cow::from("hello");
// Just pass the same reference along
awesome_greeting(&c);

let c: Cow<str> = Cow::from(String::from("hello"));
// Pass a reference to the owned string that you already have
awesome_greeting(&c);

Accepting&str让你的函数调用更加统一和方便,“最简单”的方式现在也是最高效的。这些示例也适用于Cow<[T]>etc。

于 2018-09-12T09:01:43.640 回答