为什么不鼓励接受对String(& String),Vec(& Vec)或Box(& Box)的引用作为函数参数?

时间:2016-10-12 18:50:50

标签: string reference rust borrowing

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

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

我还编写了引用VecBox的代码:

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

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

然而,我收到一些反馈意见,这样做并不是一个好主意。为什么不呢?

2 个答案:

答案 0 :(得分:110)

TL; DR:可以使用&str&[T]&T来使用更通用的代码。

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

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

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

  4. 相反,您应该接受字符串切片&str),切片&str),或仅接受参考({{ 1}})。 &[T]&T&String将分别自动强制转换为&Vec<T>&Box<T>&str

    &[T]
    &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中添加或删除项目,可以使用可变参考String或{{1 }}):

    Vec<T>
    &mut String

    专门针对切片,您也可以接受&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]

答案 1 :(得分:13)

除了Shepmaster's answer之外,接受&str(和类似的&[T]等)的另一个原因是,除以外 {{ 1}}和String也满足&str。最著名的例子之一是Deref<Target = str>,它使您可以灵活地处理自己拥有的数据还是借来的数据。

如果您有:

Cow<str>

但是您需要用fn awesome_greeting(name: &String) { println!("Wow, you are awesome, {}!", name); } 来调用它,您必须这样做:

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); 使调用您的函数更加统一和方便,并且“最简单”的方式现在也是最有效的。这些示例也可以与&str等一起使用。