rust - 为什么我可以为方法的 `&self` 参数强制引用移动语义,而不是函数参数?

标签 rust lifetime borrowing

我有一个函数的两个版本,它们的目的是做同样的事情。

版本 1 - 有效!

pub fn example1() {
    // Make a mutable slice
    let mut v = [0, 1, 2, 3];

    // Make a mutable reference to said slice
    let mut v_ref = &mut v[..];
    let len = v_ref.len();

    // Reduce slice to sub-slice -> np ;)
    v_ref = {
        // Create sub-slices
        let (v_l, v_r) = {
            // Involves some edits -> need mut
            v_ref.swap(0, len - 1);

            { v_ref }.split_at_mut(len / 2)
        };

        // Choose slice with which to overwrite
        // (involves some non-trivial condition here)
        match len % 2 {
            0 => v_l,
            _ => v_r,
        }
    };

    // And then we do something with v_ref
    println!("{:?}", v_ref);
}

本质上:

  • 我们从一个可变变量开始,mut v_ref: &mut [i32],包含对切片的可变引用
  • 我们使用 split_at_mut*
  • v_ref 制作两个子切片
  • 变量 v_ref 被覆盖以保存子切片之一

*(注意 - 我们通过移动 v_ref, as opposed to reborrowing ,使用 an identity block 避免了两个可变引用的问题)

(关于代码的意图 - 这个切片缩减旨在在循环中重复;但是这个细节不会影响问题)

版本 2 - 编译失败

版本 2 几乎与版本 1 相同,除了子切片的创建被移到了它自己的函数中:

fn example2_inner(v_ref: &mut [i32]) -> (&mut [i32], &mut [i32]) {
    // Recreate len variable in function scope
    let len = v_ref.len();

    // Same mutation here
    v_ref.swap(0, len - 1);

    // Same slice split here
    v_ref.split_at_mut(len / 2)
}

pub fn example2() {
    let mut v = [0, 1, 2, 3];

    let mut v_ref = &mut v[..];
    let len = v_ref.len();

    // This now fails to compile :(
    v_ref = {
        // Mutating & slice spliting moved to function
        let (v_l, v_r) = example2_inner({ v_ref });

        match len % 2 {
            0 => v_l,
            _ => v_r,
        }
    };

    println!("{:?}", v_ref);
}

当我尝试构建它时,出现以下错误:

error[E0506]: cannot assign to `v_ref` because it is borrowed
  --> src/lib.rs:19:5
   |
19 | /     v_ref = {
20 | |         // Mutating & slice spliting moved to function
21 | |         let (v_l, v_r) = example2_inner({ v_ref });
   | |                                           ----- borrow of `v_ref` occurs here
22 | |
...  |
26 | |         }
27 | |     };
   | |_____^ assignment to borrowed `v_ref` occurs here

error[E0502]: cannot borrow `v_ref` as immutable because `*v_ref` is also borrowed as mutable
  --> src/lib.rs:29:22
   |
21 |         let (v_l, v_r) = example2_inner({ v_ref });
   |                                           ----- mutable borrow occurs here
...
29 |     println!("{:?}", v_ref);
   |                      ^^^^^ immutable borrow occurs here
30 | }
   | - mutable borrow ends here

问题

  • 为什么这两个示例的编译方式不同?可变引用移动语义(即来自 E1 的 {vref})是否针对方法(即 split_at_mut)而非函数(即 example2_inner)强制执行>)?为什么会这样?
  • 我想将 example2_inner 保留为一个独立的效用函数:我将如何更改示例 2 以适应它?

最佳答案

你可以这样修复它:

v_ref = {
    // move `v_ref` to a new variable which will go out of scope by the end of the block
    let r = v_ref;
    // Mutating & slice splitting moved to function
    let (v_l, v_r) = example2_inner(r);

    match len % 2 {
        0 => v_l,
        _ => v_r,
    }
};

原因是 v_1v_2 都是对 v_ref 的引用,但是这些引用都比 block 长,因为它们是从它。然后你尝试写入 v_ref,借用检查器不喜欢它,因为周围仍然存在这些实时引用。

v_ref 分配给新变量 r,意味着 v_ref 不再有效 - 变量已被移动。实时引用 v_1v_2 现在引用 r,后者又是对 v 的引用,并且只在 block 中存在。您现在可以自由地写入 v_ref,因为没有其他内容指向它。


或者,您可以只更新到 Rust Edition 2018,或启用非词法生命周期,这可以处理这种情况。

关于rust - 为什么我可以为方法的 `&self` 参数强制引用移动语义,而不是函数参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52043578/

相关文章:

reference - 有没有办法返回对函数中创建的变量的引用?

rust - 当输入非常清楚时,为什么借用检查器需要输出生命周期标签?

rust - 如何为将作为闭包参数的关联类型指定生存期?

rust - 无法借用对捕获树中结构的引用,因为它的生命周期不够长

rust - 借用 Rc<RefCell<T>> 中的 T

string - 如何将 to_string() 功能添加到枚举中?

string - 返回结果,带无引号的字符串

rust - 如何获取子切片?

path - 如何编写存储路径的构建器?

iterator - Rust 检查迭代器 : cannot borrow `*` as immutable because it is also borrowed as mutable