不借用的API在哪些场景下是首选?
Posted
技术标签:
【中文标题】不借用的API在哪些场景下是首选?【英文标题】:In what scenarios are APIs that don't borrow preferred? 【发布时间】:2019-05-17 03:39:34 【问题描述】: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);
由于email
和username
已被移动,因此在调用build_user
后它们将无法再使用。
这可以通过让 API 使用借用的引用来解决。
考虑到这一点,在设计 API 时,哪些场景总是倾向于不使用借用?
【问题讨论】:
函数build_user()
将参数移动到结构User
中。如果您想使用借用编写相同的函数,则不能将它们移动到结构中,因为您不拥有它们,因此您必须克隆它们(或更改 User
的定义以存储借用) .
我认为这个问题对于 *** QA 格式来说有点太宽泛太模糊了。
感谢 cmets!不幸的是,他们似乎没有解决我的问题。如果它太宽泛,我很乐意让它更具体。有什么建议?我只是想知道为什么或何时想要创建一个不借用的 API。必须有案例。
我的评论并不是作为一个答案,而是要指出你声称你也可以使用借用的说法是不正确的。作为推论,这个函数是一个通常按值获取参数的函数的示例,因为它需要所有权。如果您想要更多示例,只需查看标准库即可。按值接受参数的函数通常不能写成借用。
之所以我认为这个过于笼统,是因为回答这个问题基本上是为了解释 Rust 的所有权制度。对此已有很好的介绍,例如 Rust 书中关于所有权的章节。
【参考方案1】:
此列表可能并不详尽,但很多时候选择不借用论点是有利的。
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
两次。实现不必在运行时检查未来是否已经处于等待状态 - 编译器不会允许这种情况。
它还可以在使用方法链构建器时防止错误。该设计静态地防止您乱序操作 - 您不会在创建对象后意外地在构建器上设置字段,因为构建器被其 build
方法使用。
5。使克隆对调用者明确
有些函数需要拥有自己的数据。这可以通过接受一个引用然后在函数中调用clone
来强制执行,但这可能并不总是理想的,因为它对调用者隐藏了潜在的昂贵的克隆操作。接受该值而不是引用意味着由调用者来克隆该值,或者,如果他们不再需要它,则改为移动它。
【讨论】:
以上是关于不借用的API在哪些场景下是首选?的主要内容,如果未能解决你的问题,请参考以下文章