我一直在阅读类似 Why does a function that accepts a Box<MyType> complain of a value being moved when a function that accepts self works? 的问题, Preferable pattern for getting around the "moving out of borrowed self" checker , 和 How to capture self consuming variable in a struct? ,现在我很好奇消费 self 但可能将其返回给调用者的性能特征。
举一个更简单的例子,假设我想创建一个保证非空的集合类型。为此,“删除”操作需要使用集合并有选择地返回自身。
struct NonEmptyCollection { ... }
impl NonEmptyCollection {
fn pop(mut self) -> Option<Self> {
if self.len() == 1 {
None
} else {
// really remove the element here
Some(self)
}
}
}
(我想它也应该返回它从列表中删除的值,但这只是一个例子。)现在假设我调用这个函数:
let mut c = NonEmptyCollection::new(...);
if let Some(new_c) = c.pop() {
c = new_c
} else {
// never use c again
}
对象的内存实际上发生了什么?如果我有这样的代码怎么办:
let mut opt: Option<NonEmptyCollection> = Some(NonEmptyCollection::new(...));
opt = opt.take().pop();
函数的签名并不能保证返回的对象实际上是同一个对象,那么有什么优化可能呢? C++ 返回值优化之类的东西是否适用,允许返回的对象在它之前所在的同一内存中“构造”?如果我可以在上面的接口(interface)和调用者必须处理生命周期的接口(interface)之间进行选择:
enum PopResult {
StillValid,
Dead
};
impl NonEmptyCollection {
fn pop(&mut self) -> PopResult {
// really remove the element
if self.len() == 0 { PopResult::Dead } else { PopResult::StillValid }
}
}
出于性能原因,是否有理由选择这种较脏的接口(interface)?在answer to the second example I linked , trentcl 建议将 Option
存储在数据结构中,以允许调用者就地进行更改,而不是每次执行 remove
后跟 insert
时间。这个肮脏的接口(interface)会是一个更快的选择吗?
最佳答案
YMMV
根据优化器的突发奇想,您最终可能会得到:
- 接近空操作,
- 一些注册 Action ,
- 一些位拷贝。
这将取决于是否:
- 调用是否内联,
- 调用者重新分配给原始变量或创建一个新变量(以及 LLVM 如何处理重用死空间),
size_of::<Self>()
.
你得到的唯一保证是不会发生深拷贝,因为没有 .clone()
打电话。
对于其他任何事情,您需要检查 LLVM IR 或程序集。
关于rust - 消费 self 并返回它对性能有何影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48226014/