ide*_*n42 1 rust borrow-checker
如何在没有Rust移动第一次访问的情况下访问选项中的向量?
fn maybe_push(v_option: Option<&mut Vec<usize>>) -> usize {
let mut c = 0;
if let Some(v) = v_option {
for i in 0..10 {
v.push(i);
c += i;
}
} else {
for i in 0..10 {
c += i;
}
}
// second access, fails
if let Some(v) = v_option {
for i in 10..20 {
v.push(i);
c += i;
}
} else {
for i in 10..20 {
c += i;
}
}
return c;
}
fn main() {
let mut v: Vec<usize> = vec![];
println!("{}", maybe_push(Some(&mut v)));
println!("{}", maybe_push(None));
println!("{:?}", v);
}
Run Code Online (Sandbox Code Playgroud)
这给出了错误:
error[E0382]: use of partially moved value: `v_option`
--> src/main.rs:16:22
|
4 | if let Some(v) = v_option {
| - value moved here
...
16 | if let Some(v) = v_option {
| ^^^^^^^^ value used here after move
Run Code Online (Sandbox Code Playgroud)
if let Some(ref mut v) = v_option {建议的使用也失败了:
error: cannot borrow immutable anonymous field `(v_option:std::prelude::v1::Some).0` as mutable
--> src/main.rs:4:21
|
4 | if let Some(ref mut v) = v_option {
| ^^^^^^^^^
error: cannot borrow immutable anonymous field `(v_option:std::prelude::v1::Some).0` as mutable
--> src/main.rs:17:21
|
17 | if let Some(ref mut v) = v_option {
| ^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
按值匹配会将值移动到模式变量中.移动使原始值不可用,除了实现Copy特征的非常简单的对象,例如数字.与C中的指针不同,可变引用不可复制,这可以在以下不编译的示例中看到:
let mut v = vec![1, 2, 3];
let rv = &mut v; // mutable reference to v
{
// move rv to r1, r1 now becomes the sole mutable reference to v
let r1 = rv;
r1.push(4);
}
{
let r2 = rv; // error: rv was already moved to r1
r2.push(5);
}
Run Code Online (Sandbox Code Playgroud)
Rust拒绝上述内容,因为它强制执行禁止对对象进行多个可变引用的一般规则.尽管这个特定的代码片段是安全的,允许同时对同一个对象进行多个可变引用,这样可以很容易地编写Rust明确设计用来防止的那种不安全的程序,例如那些包含多线程代码中的数据争用的程序,或那些通过无效的迭代器访问数据.因此,分配let r1 = rv只能移动的rv参考r1,则不允许let r2 = rv它现在是指移动变量声明rv.
要修复代码,我们必须创建对引用的单独引用.这将创建对原始引用的两个不同的可变引用,而不是将原始可变引用移动到内部范围:
let mut v = vec![1, 2, 3];
let mut rv = &mut v;
{
// rr1 is a *new* mutable reference to rv - no move is performed
let rr1 = &mut rv;
rr1.push(4);
}
{
// rr2 is a *separate* new mutable reference to rv - also no move
let rr2 = &mut rv;
rr2.push(5);
}
Run Code Online (Sandbox Code Playgroud)
语法push调用是相同的r1.push,并rr1.push因为锈病的.操作会自动取消引用任何引用数.
从问题返回的例子中,参照Vec<usize>在Option是像v上述参考文献,以及使用该匹配它Some(v)模式来移动参考入v图案的变量.
为了修正它,图案必须改变如在上面的例子中,指定的变量是指,其本身是一个参考值.这是使用if let Some(ref mut v)语法实现的.如上所述,在rv声明必须更改为可变的情况下,此修复程序还要求它Option是可变的.通过这两个更改,代码编译:
fn maybe_push(mut v_option: Option<&mut Vec<usize>>) -> usize {
let mut c = 0;
if let Some(ref mut v) = v_option {
for i in 0..10 {
v.push(i);
c += i;
}
} else {
for i in 0..10 {
c += i;
}
}
if let Some(ref mut v) = v_option {
for i in 10..20 {
v.push(i);
c += i;
}
} else {
for i in 10..20 {
c += i;
}
}
return c;
}
fn main() {
let mut v: Vec<usize> = vec![];
println!("{}", maybe_push(Some(&mut v)));
println!("{}", maybe_push(None));
println!("{:?}", v);
}
Run Code Online (Sandbox Code Playgroud)
另一种可能性是使用该as_mut()方法将选项内容作为对先前内容的可变引用返回.这在概念上等同于从第一个片段let r1 = rv到let rr1 = &mut rv第一个片段的变化,并允许使用if let Some(v)模式,其中v仍然是对向量的可变引用的引用.这也要求v_option声明为可变.