什么场景下优先使用不借用的API?
In what scenarios are APIs that don't borrow preferred?
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 时,人们总是希望在哪些情况下不使用借用?
此列表可能并不详尽,但有很多时候选择不借用参数是有利的。
1。小型 Copy
类型
的效率
如果一个类型很小并且实现了 Copy
,通常复制它比传递指针更有效。引用意味着间接 - 除了必须执行两个步骤来获取数据之外,指针后面的值不太可能紧凑地存储在内存中,因此复制到 CPU 缓存中的速度较慢,例如,如果您正在迭代他们。
2。转让所有权
当您需要保留数据,但当前所有者需要清理并超出范围时,您可以通过将其移动到其他地方来转移所有权。例如,您可能在函数中有一个局部变量,但将其移动到 Box
中,以便它可以在函数 returned 后继续存在。
3。方法链接
如果一组方法都消耗 self
和 return 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
来强制执行,但这可能并不总是理想的,因为它向调用者隐藏了潜在的昂贵的克隆操作。接受值而不是引用意味着由调用者克隆值,或者如果他们不再需要它,则移动它。
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 时,人们总是希望在哪些情况下不使用借用?
此列表可能并不详尽,但有很多时候选择不借用参数是有利的。
1。小型 Copy
类型
的效率
如果一个类型很小并且实现了 Copy
,通常复制它比传递指针更有效。引用意味着间接 - 除了必须执行两个步骤来获取数据之外,指针后面的值不太可能紧凑地存储在内存中,因此复制到 CPU 缓存中的速度较慢,例如,如果您正在迭代他们。
2。转让所有权
当您需要保留数据,但当前所有者需要清理并超出范围时,您可以通过将其移动到其他地方来转移所有权。例如,您可能在函数中有一个局部变量,但将其移动到 Box
中,以便它可以在函数 returned 后继续存在。
3。方法链接
如果一组方法都消耗 self
和 return 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
来强制执行,但这可能并不总是理想的,因为它向调用者隐藏了潜在的昂贵的克隆操作。接受值而不是引用意味着由调用者克隆值,或者如果他们不再需要它,则移动它。