如何交换可变引用的值,暂时取得所有权?

ddu*_*ney 1 rust borrow-checker

我有一个函数可以获取某些数据的所有权,对其进行破坏性修改,然后返回它。

fn transform(s: MyData) -> MyData {
    todo!()
}
Run Code Online (Sandbox Code Playgroud)

在某些情况下,我有一个&mut MyData参考。我想transform申请&mut MyData.

fn transform_mut(data_ref: &mut MyData) {
    *data_ref = transform(*data_ref);
}
Run Code Online (Sandbox Code Playgroud)

铁锈游乐场

但是,这会导致编译器错误。

error[E0507]: cannot move out of `*data_ref` which is behind a mutable reference
  --> src/lib.rs:10:27
   |
10 |     *data_ref = transform(*data_ref);
   |                           ^^^^^^^^^ move occurs because `*data_ref` has type `MyData`, which does not implement the `Copy` trait
Run Code Online (Sandbox Code Playgroud)

我考虑过使用mem::swapmem::replace,但它们要求您在取出另一个值之前已经有一些有效的值可以放入参考中。

有什么办法可以做到这一点吗?MyData没有合理的默认值或虚拟值来临时存储在引用中。感觉因为我拥有独占访问权限,所以所有者不应该关心转换,但我的直觉在这里可能是错误的。

Kev*_*eid 5

\n

感觉因为我有独占访问权限,所有者不应该关心转换,但我的直觉可能是错误的。

\n
\n

这个想法的问题是,如果允许这样做,并且函数transform发生恐慌,则 中不再有有效值*data_ref,如果展开被捕获Drop或在内存处理内部,则该值可见data_ref则该值是可见的。

\n

例如,让我们以明显的方式实现这一点,只需将数据从引用对象中复制出来并返回:

\n
use std::ptr;\n\nfn naive_modify_in_place<T>(place: &mut T, f: fn(T) -> T) {\n    let mut value = unsafe { ptr::read(place) };\n    value = f(value);\n    unsafe { ptr::write(place, value) };\n}\n\nfn main() {\n    let mut x = Box::new(1234);\n    naive_modify_in_place(&mut x, |x| panic!("oops"));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果您运行此程序(Rust Playground 链接),它将崩溃并出现 \xe2\x80\x9cdouble free\xe2\x80\x9d 错误。这是因为从恐慌函数中展开会丢弃它的参数,然后从main丢弃的x\xe2\x80\x94 中展开,这是同一个盒子,已经释放了。

\n

有几个专门设计来解决这个问题的 crate:\n take_mut,并replace_with打算对其进行改进。(我特别没有使用过。)这两者都提供了两种处理恐慌的选项:

\n
    \n
  1. 强制中止(程序立即退出,无法处理恐慌或清理其他任何内容)。
  2. \n
  3. 将引用的指示对象替换为不同的新计算值,因为恐慌开始时前一个值已丢失。
  4. \n
\n

当然,如果您没有有效的替代值,那么您就不能采用选项 2。在这种情况下,您可能需要考虑通过添加占位符来完全绕过这种情况:如果您可以存储Option<MyData>并传递 an &mut Option<MyData>,那么您的代码可以用来Option::take暂时删除该值并保留None在原来的位置。仅当发生恐慌时,它们None才会可见,并且如果您的代码没有捕获恐慌,那么它就永远不会重要。但这确实意味着对数据的每次访问都需要从Option(例如使用.as_ref().unwrap())中检索它。

\n