什么情况下不需要使用借用API?

13

Rust有所有权和借用的概念。如果一个函数不将其参数作为引用进行借用,那么传递给该函数的参数会被移动,并在超出范围后被回收。

考虑这个函数:

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
    }
}

此函数可以这样调用:

let email = String::from("foo@example.com");
let username = String::from("username");

let user = build_user(email, username);

build_user调用后,由于emailusername已被移动,因此无法再使用它们。

可以通过使用借用引用来解决这个问题。

考虑到这一点,设计API时有哪些情况下我们总是更愿意不使用借用?


函数 build_user() 将参数移动到结构体 User 中。如果您想使用借用编写相同的函数,则无法将它们移动到结构体中,因为您不拥有它们,所以您必须克隆它们(或更改 User 的定义以存储借用而不是所有权)。 - Sven Marnach
2
我认为这个问题对于StackOverflow的问答格式来说有些过于宽泛和模糊。 - Sven Marnach
1
我认为这个问题太宽泛的原因是回答这个问题基本上意味着要解释Rust的所有权系统。已经有很好的介绍了,例如Rust书中关于所有权的章节。 - Sven Marnach
1
只需看一下使用借用而不是移动值的基本相同函数。你需要使用to_owned()复制字符串才能使其工作,如果你不再需要传入的字符串,则这种方法效率低下。另一方面,如果你编写接受值的函数,则可以自由地传递克隆值,如果仍然需要这些值。 - Sven Marnach
啊,有趣!我得研究一下 to_owned() 和生命周期才能完全理解这个 :) 感谢您的反馈! - Pascal Precht
显示剩余3条评论
1个回答

26

这个列表可能不是详尽无遗的,但有很多时候选择不借用一个参数会更有优势。

1. 对于小的Copy类型,效率更高

如果一个类型很小并实现了Copy,通常复制它比传递指针更有效。引用意味着间接 - 除了必须执行两步才能到达数据外,指针后面的值不太可能被紧密地存储在内存中,因此将其复制到CPU高速缓存中会更慢,例如当您对它们进行迭代时。

2. 转移所有权

当您需要数据保留下来,但当前所有者需要被清理并超出作用域时,您可能需要通过将其移动到其他地方来转移所有权。例如,您可能在函数中有一个局部变量,但将其移动到Box中,以便可以在函数返回后继续存在。

3. 方法链接

如果一组方法都使用self并返回Self,则可以方便地将它们链接在一起,而无需中间本地变量。您经常会看到此方法用于实现生成器。以下示例摘自derive_builder crate文档:

let ch = ChannelBuilder::default()
    .special_info(42u8)
    .token(19124)
    .build()
    .unwrap();

4. 静态地强制不变量

有时候,您希望一个值被函数消耗以保证它不能再次使用,这是在类型级别实施假设的一种方式。例如,在futures crate中,Future::wait方法会消耗self

fn wait(self) -> Result<Self::Item, Self::Error> 
where
    Self: Sized,

这个签名特别设计用来防止你调用wait两次。实现不需要在运行时检查future是否已经处于等待状态——编译器就不允许这种情况。

它还可以在使用方法链构建器时防止错误。设计静态地防止你按顺序做出错误的操作——在创建对象之后,你不能意外地在构建器上设置字段,因为构建器被其build方法消耗了。

5. 显式地向调用者表示克隆

某些函数需要拥有它们的数据。这可以通过接受引用并在函数内部调用clone来强制执行,但这可能并不总是理想的,因为它会将潜在昂贵的克隆操作隐藏在调用者中。接受值而不是引用意味着由调用者来克隆该值或者如果他们不再需要它,移动它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接