我有两个案例枚举:
#[derive(Debug)]
pub enum Enum {
Str(String),
Fields { len: u32, opt: Option<String> },
}
use Enum::*;
我希望根据其值来就地更新我的枚举。这首先起作用:
pub fn update(x: &mut Enum) {
match x {
&mut Str(ref mut s) => { s.push('k'); }
&mut Fields { ref mut len, ref mut opt } => { *len += 1; }
}
}
我想在某些情况下切换枚举类型:
pub fn update(x: &mut Enum) {
match x {
&mut Str(ref mut s) => { s.push('k'); }
&mut Fields { ref mut len, ref mut opt } => {
if *len < 5 {
*x = Str(String::from("default"));
} else {
*len += 1;
}
}
}
}
现在借阅检查员不高兴:
error[E0506]: cannot assign to `*x` because it is borrowed
--> src/main.rs:14:15
|
12 | &mut Fields { ref mut len, ref mut opt } => {
| ----------- borrow of `*x` occurs here
13 | if *len < 5 {
14 | *x = Str(String::from("default"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `*x` occurs here
在这种情况下,我们可以通过分配临时变量来解决问题:
pub fn update(x: &mut Enum) {
let mut update_hack: Option<Enum> = None;
match x {
&mut Str(ref mut s) => { s.push('k'); }
&mut Fields { ref mut len, ref mut opt } => {
if *len < 5 {
update_hack = Some(Str(String::from("default")));
} else {
*len += 1;
}
}
}
match update_hack {
None => {},
Some(to_return) => { *x = to_return; },
}
}
但我想在update_hack
中使用我的一些数据。
match x {
&mut Str(ref mut s) => { s.push('k'); }
&mut Fields { ref mut len, ref mut opt } => {
match opt {
&mut Some(ref s) => { update_hack = Some(Str(*s)) },
&mut None => { *len += 1 },
}
}
}
现在我们遇到了更严重的麻烦:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:16:62
|
16 | &mut Some(ref s) => { update_hack = Some(Str(*s)) },
| ^^ cannot move out of borrowed content
这次我不确定如何解决这个问题。当应该有一个干净的方式时,感觉好像我们正在乱堆。什么是惯用的解决方案?
答案 0 :(得分:4)
从Rust 1.23.0开始,借用总是有词汇。从此匹配块创建的x
的可变引用:
match x {
&mut Str(ref mut s) => { s.push('k'); }
&mut Fields { ref mut len, ref mut opt } => {
if *len < 5 {
*x = Str(String::from("default"));
} else {
*len += 1;
}
}
}
直到代码中的最后一个}
。
通常,使用另一个{}
来避免此问题,例如:
let mut x = String::from("a");
{
let y = &mut x;
*y = String::from("b");
}
// & to be explicit
println!("borrows x: {}", &x);
这个技巧有效,因为它限制了词汇范围。你可以做到
pub fn update(x: &mut Enum) {
let s = match *x {
Str(ref mut s) => {
s.push('k');
return;
}
Fields {
ref mut len,
ref opt,
} if *len >= 5 || opt.is_none() =>
{
*len += 1;
return;
}
// opt is Some(str)
// Option.take() is used to get String out of
// &mut Option<String>
Fields {
len: _,
ref mut opt,
} => opt.take().unwrap(),
};
*x = Str(s);
}
如果您使用夜间工具链和#![feature(nll)]
,您的代码几乎可以使用。唯一的区别是必须使用Option::take()
。
match *x {
Str(ref mut s) => {
s.push('k');
}
Fields {
ref mut len,
ref mut opt,
} => {
if *len < 5 {
// Use Option::take() if you want to move owned value
// out of &mut Option<String>
match opt.take() {
Some(s) => *x = Str(s),
None => *len += 1,
}
} else {
*len += 1;
}
}
}
https://play.rust-lang.org/?gist=235580d4be1242fc9dd3a7e4ee5fe186&version=nightly