[英]How do I mutate in a match which borrows an immutable value?
I can understand borrowing/ownership concepts in Rust, but I have no idea how to work around this case:我可以理解 Rust 中的借用/所有权概念,但我不知道如何解决这种情况:
use std::collections::{HashMap, HashSet};
struct Val {
t: HashMap<u16, u16>,
l: HashSet<u16>,
}
impl Val {
fn new() -> Val {
Val {
t: HashMap::new(),
l: HashSet::new(),
}
}
fn set(&mut self, k: u16, v: u16) {
self.t.insert(k, v);
self.l.insert(v);
}
fn remove(&mut self, v: &u16) -> bool {
self.l.remove(v)
}
fn do_work(&mut self, v: u16) -> bool {
match self.t.get(&v) {
None => false,
Some(r) => self.remove(r),
}
}
}
fn main() {
let mut v = Val::new();
v.set(123, 100);
v.set(100, 1234);
println!("Size before: {}", v.l.len());
println!("Work: {}", v.do_work(123));
println!("Size after: {}", v.l.len());
}
The compiler has the error:编译器有错误:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:28:24
|
26 | match self.t.get(&v) {
| ------ immutable borrow occurs here
27 | None => false,
28 | Some(r) => self.remove(r),
| ^^^^^------^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
I don't understand why I can't mutate in the match arm when I did a get
(read value) before;我不明白为什么我之前执行
get
(读取值)时无法在 match 臂中发生变异; the self.t.get
is finished when the mutation via remove
begins. self.t.get
在通过remove
开始突变时完成。
Is this due to scope of the result ( Option<&u16>
) returned by the get
?这是由于
get
返回的结果( Option<&u16>
)的范围吗? It's true that the lifetime of the result has a scope inside the match expression, but this design-pattern is used very often (mutate in a match expression).结果的生命周期确实在匹配表达式内有一个范围,但这种设计模式经常使用(在匹配表达式中发生变异)。
How do I work around the error?我该如何解决该错误?
The declaration of function HashMap::<K,V>::get()
is, a bit simplified:函数
HashMap::<K,V>::get()
的声明有点简化:
pub fn get<'s>(&'s self, k: &K) -> Option<&'s V>
This means that it returns an optional reference to the contained value, not the value itself.这意味着它返回对所包含值的可选引用,而不是值本身。 Since the returned reference points to a value inside the map, it actually borrows the map, that is, you cannot mutate the map while this reference exists.
由于返回的引用指向映射内部的一个值,它实际上是借用了映射,也就是说,当这个引用存在时,你不能改变映射。 This restriction is there to protect you, what would happen if you remove this value while the reference is still alive?
这个限制是为了保护你,如果你在引用还活着的时候删除这个值会发生什么?
So when you write:所以当你写:
match self.t.get(&v) {
None => false,
//r: &u16
Some(r) => self.remove(r)
}
the captured r
is of type &u16
and its lifetime is that of self.t
, that is, it is borrowing it.捕获的
r
是&u16
类型,它的生命周期是self.t
生命周期,也就是说,它正在借用它。 Thus you cannot get a mutable reference to self
, that is needed to call remove.因此,您无法获得对
self
的可变引用,这是调用 remove 所必需的。
The simplest solution for your problem is the clone()
solves every lifetime issue pattern.您的问题最简单的解决方案是
clone()
解决每个生命周期问题模式。 Since your values are of type u16
, that is Copy
, it is actually trivial:由于您的值属于
u16
类型,即Copy
,因此实际上很简单:
match self.t.get(&v) {
None => false,
//r: u16
Some(&r) => self.remove(&r)
}
Now r
is actually of type u16
so it borrows nothing and you can mutate self
at will.现在
r
实际上是u16
类型,所以它什么都不借用,你可以随意改变self
。
If your key/value types weren't Copy
you could try and clone
them, if you are willing to pay for that.如果您的键/值类型不是
Copy
您可以尝试clone
它们,如果您愿意为此付费。 If not, there is still another option as your remove()
function does not modify the HashMap
but an unrelated HashSet
.如果没有,还有另一种选择,因为您的
remove()
函数不会修改HashMap
而是修改不相关的HashSet
。 You can still mutate that set if you take care not to reborrow self
:如果您注意不要重新借用
self
,您仍然可以改变该集合:
fn remove2(v: &u16, l: &mut HashSet<u16>) -> bool {
l.remove(v)
}
fn do_work(&mut self, v: u16) -> bool {
match self.t.get(&v) {
None => false,
//selt.t is borrowed, now we mut-borrow self.l, no problem
Some(r) => Self::remove2(r, &mut self.l)
}
}
You are trying to remove value from HashMap
by using value
you get, not key
.您正在尝试使用您获得的
value
而不是key
从HashMap
删除值。
Only line 26 is changed Some(_) => self.remove(&v)
仅第 26 行更改
Some(_) => self.remove(&v)
This will work:这将起作用:
use std::collections::HashMap;
struct Val {
t: HashMap<u16, u16>
}
impl Val {
fn new() -> Val {
Val { t: HashMap::new() }
}
fn set(&mut self, k: u16, v: u16) {
self.t.insert(k, v);
}
fn remove(&mut self, v: &u16) -> bool {
match self.t.remove(v) {
None => false,
_ => true,
}
}
fn do_work(&mut self, v: u16) -> bool {
match self.t.get(&v) {
None => false,
Some(_) => self.remove(&v)
}
}
}
fn main() {
let mut v = Val::new();
v.set(123, 100);
v.set(1100, 1234);
println!("Size before: {}", v.t.len());
println!("Work: {}", v.do_work(123));
println!("Size after: {}", v.t.len());
}
It seems that the following solution is good for primitive types like here u16.似乎以下解决方案适用于像这里 u16 这样的原始类型。 For other types, the ownership is moved.
对于其他类型,所有权已转移。
use std::collections::HashMap;
struct Val {
t: HashMap<u16, u16>,
}
impl Val {
fn new() -> Val {
Val { t: HashMap::new() }
}
fn set(&mut self, k: u16, v: u16) {
self.t.insert(k, v);
}
fn remove(&mut self, v: &u16) -> bool {
match self.t.remove(v) {
None => false,
_ => true,
}
}
fn do_work(&mut self, v: u16) -> bool {
match self.t.get(&v) {
None => false,
Some(&v) => self.remove(&v)
}
}
}
fn main() {
let mut v = Val::new();
v.set(123, 100);
v.set(100, 1234);
println!("Size before: {}", v.t.len());
println!("Work: {}", v.do_work(123));
println!("Size after: {}", v.t.len());
}
For other types, we must clone the value:对于其他类型,我们必须克隆值:
use std::collections::{HashMap, HashSet};
#[derive(Debug)]
struct Val {
t: HashMap<String, String>,
l: HashSet<String>
}
impl Val {
fn new() -> Val {
Val { t: HashMap::new(), l: HashSet::new() }
}
fn set(&mut self, k: String, v: String) {
self.l.insert(v.clone());
self.t.insert(k, v);
}
fn remove(&mut self, v: &String) -> bool {
self.l.remove(v)
}
fn do_work(&mut self, i: &String) -> bool {
match self.t.get(i) {
None => false,
Some(v) => {
let x = v.clone();
self.remove(&x)
}
}
}
fn do_task(&mut self, i: &String) -> bool {
match self.t.get(i) {
None => false,
Some(v) => self.l.insert(v.clone())
}
}
}
fn main() {
let mut v = Val::new();
v.set("AA".to_string(), "BB".to_string());
v.set("BB".to_string(), "CC".to_string());
println!("Start: {:#?}", v);
println!("Size before: {}", v.l.len());
println!("Work: {}", v.do_work(&"AA".to_string()));
println!("Size after: {}", v.l.len());
println!("After: {:#?}", v);
println!("Task [Exist]: {}", v.do_task(&"BB".to_string()));
println!("Task [New]: {}", v.do_task(&"AA".to_string()));
println!("End: {:#?}", v);
}
But i'd like a solution that has no allocation但我想要一个没有分配的解决方案
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.