【问题标题】:How can I swap out the value of a mutable reference, temporarily taking ownership? [duplicate]如何交换可变引用的值,暂时获得所有权? [复制]
【发布时间】:2021-07-16 21:40:57
【问题描述】:

我有一个函数,它获取一些数据的所有权,破坏性地修改它,然后返回它。

fn transform(s: MyData) -> MyData {
    todo!()
}

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

fn transform_mut(data_ref: &mut MyData) {
    *data_ref = transform(*data_ref);
}

Rust Playground

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

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

我考虑过使用mem::swapmem::replace,但它们要求您已经有一些有效值可以放入引用中,然后再取出另一个。

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

【问题讨论】:

    标签: rust borrow-checker


    【解决方案1】:

    感觉好像是因为我拥有独占访问权限,所以所有者不应该关心转换,但我的直觉可能在这里错了。

    这个想法的问题在于,如果允许这样做,并且函数transform 发生恐慌,则*data_ref 中不再有有效值,如果the unwind is caughtDrop 内部处理内存data_ref指向。

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

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

    如果你运行这个程序 (Rust Playground link),它会因为“double free”错误而崩溃。这是因为从恐慌函数展开时丢弃了它的参数,然后从 main 展开时丢弃了 x — 这是同一个框,已被释放。

    有几个专门为解决这个问题而设计的板条箱: take_mutreplace_with 旨在改进它。 (我也没有使用过,特别是。)这两个都提供了两种处理恐慌的选项:

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

    当然,如果您没有有效的替代值,那么您不能选择选项 2。在这种情况下,您可能想考虑通过添加完全绕过这种情况> 占位符:如果您可以存储Option&lt;MyData&gt; 并传递&amp;mut Option&lt;MyData&gt;,那么您的代码可以使用Option::take 临时删除该值并将None 保留在其位置。 None 只有在出现恐慌时才可见,并且如果您的代码没有捕捉到恐慌,那么它永远不会有关系。但这确实意味着对数据的每次访问都需要从Option 中检索它(例如使用.as_ref().unwrap())。

    【讨论】:

      【解决方案2】:

      您可以为MyData 派生(或实现)Clone,然后将克隆传递给您的破坏性转换。

      https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e3a4d737a700ef6baa35160545f0e514

      克隆消除了数据在引用后面的问题。

      【讨论】:

        猜你喜欢
        • 2021-12-21
        • 2012-12-07
        • 2018-04-10
        • 1970-01-01
        • 1970-01-01
        • 2013-02-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多