与复制类型的借用特征相反?

Dav*_*lis 5 traits ownership rust borrowing

我见过Borrow用于定义既接受拥有的类型也接受引用的函数的特征,例如Tor &Tborrow()然后在函数中调用该方法以获取&T.

是否有一些特性允许类型相反(即接受T&T获得的函数TCopy

例如对于这个例子:

use std::borrow::Borrow;

fn foo<T: Borrow<u32>>(value: T) -> u32 {
    *value.borrow()
}

fn main() {
    println!("{}", foo(&5));
    println!("{}", foo(5));
}
Run Code Online (Sandbox Code Playgroud)

这需要borrow()获取一个引用,然后立即取消引用。

是否有另一个实现只复制T传入的值,并取消引用如果&T给出?或者上面是写这种东西的惯用方式?

tre*_*tcl 5

并没有真正的逆特征Borrow,因为它并没有像函数那样真正有用Borrow。原因与所有权有关。

为什么“逆Borrow”不如Borrow

需要引用的函数

考虑一个只需要引用其参数的函数:

fn puts(arg: &str) {
    println!("{}", arg);
}
Run Code Online (Sandbox Code Playgroud)

String在这里接受是愚蠢的,因为puts不需要获得数据的所有权,但接受&str意味着我们有时可能会强制调用者将数据保留的时间超过必要的时间:

{
    let output = create_some_string();
    output.push_str(some_other_string);
    puts(&output);
    // do some other stuff but never use `output` again
} // `output` isn't dropped until here
Run Code Online (Sandbox Code Playgroud)

问题是output在传递给 之后不需要它puts,调用者知道这一点,但puts需要引用,所以output必须保持活动直到块结束。显然,您始终可以通过添加更多块(有时是 a )来在调用者中解决此问题let,但puts也可以将其设为通用以让调用者委派清理责任output

fn puts<T: Borrow<str>>(arg: T) {
    println!("{}", arg.borrow());
}
Run Code Online (Sandbox Code Playgroud)

接受T: Borrowforputs使调用者可以灵活地决定是保留参数还是将其移动到函数中。¹

需要拥有值的函数

现在考虑一个实际需要获得所有权的函数的情况:

struct Wrapper(String);
fn wrap(arg: String) -> Wrapper {
    Wrapper(arg)
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,接受&str将是愚蠢的,因为wrap必须调用to_owned()它。如果调用者有一个String不再使用的 ,那将不必要地复制可能刚刚移动到函数中的数据。在这种情况下,接受String是更灵活的选项,因为它允许调用者决定是进行克隆还是传递现有的String. 拥有“反向Borrow”特征不会增加任何arg: String尚未提供的灵活性。

String并不总是最符合人体工程学的论点,因为有几种不同类型的字符串:&str, Cow<str>, Box<str>... 我们可以wrap通过说它接受任何可以转换intoString.

fn wrap<T: Into<String>>(arg: T) -> Wrapper {
    Wrapper(arg.into())
}
Run Code Online (Sandbox Code Playgroud)

这意味着您可以随意调用它,wrap("hello, world")而无需调用.to_owned()文字。这并不是真正的灵活性胜利——调用者总是可以调用.into()而不失一般性——但它是符合人体工程学的胜利。

什么Copy类型的?

现在,您询问了Copy类型。在大多数情况下,上述论点仍然适用。如果您正在编写一个函数,比如puts,只需要一个&A, usingT: Borrow<A>对调用者来说可能更灵活;对于像wrap这样需要整体的功能,A只接受更灵活A。但是对于Copy类型来说,接受的人体工程学优势T: Into<A>要少得多。

  • 对于整数类型,因为泛型会干扰类型推断,所以使用它们通常会使使用文字变得符合人体工程学;您最终可能不得不显式注释类型。
  • 由于&u32没有实现Into<u32>,那个特殊的技巧在这里无论如何都不起作用。
  • 由于Copy类型很容易作为拥有的值使用,因此首先通过引用使用它们并不常见。
  • 最后,将 a&A变成AwhenA: Copy就像添加*; 在大多数情况下,能够跳过这一步可能不足以抵消使用泛型增加的复杂性。

总之,foo几乎可以肯定,应该接受value: u32并让调用者决定如何获得该值。

也可以看看


¹ 对于您可能需要的这个特定函数AsRef<str>,因为您不依赖于 的额外保证Borrow,而且事实上所有T实现Borrow<T>通常与str. 但这是无关紧要的。