我有一个函数的两个版本,它们的目的是做同样的事情。
版本 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_1
和 v_2
都是对 v_ref
的引用,但是这些引用都比 block 长,因为它们是从它。然后你尝试写入 v_ref
,借用检查器不喜欢它,因为周围仍然存在这些实时引用。
将 v_ref
分配给新变量 r
,意味着 v_ref
不再有效 - 变量已被移动。实时引用 v_1
和 v_2
现在引用 r
,后者又是对 v
的引用,并且只在 block 中存在。您现在可以自由地写入 v_ref
,因为没有其他内容指向它。
或者,您可以只更新到 Rust Edition 2018,或启用非词法生命周期,这可以处理这种情况。
关于rust - 为什么我可以为方法的 `&self` 参数强制引用移动语义,而不是函数参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52043578/