我如何在借用不可变值的匹配中发生变异?
How do I mutate in a match which borrows an immutable value?
我可以理解 borrowing/ownership 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());
}
编译器出现错误:
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
我不明白为什么我之前做get
(读取值)时不能在match arm中变异;当通过 remove
的突变开始时,self.t.get
完成。
这是由于 get
返回的结果 (Option<&u16>
) 的范围造成的吗?结果的生命周期确实在匹配表达式内有一个范围,但这种设计模式经常使用(在匹配表达式中变异)。
如何解决该错误?
您正在尝试通过使用您获得的 value
而不是 key
从 HashMap
中删除值。
只更改了第 26 行 Some(_) => self.remove(&v)
这会起作用:
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());
}
函数HashMap::<K,V>::get()
的声明有点简化:
pub fn get<'s>(&'s self, k: &K) -> Option<&'s V>
这意味着它 returns 对包含值的可选引用,而不是值本身。由于返回的引用指向地图内部的一个值,它实际上是借用了地图,也就是说,当这个引用存在时你不能改变地图。这个限制是为了保护你,如果你在引用还活着的时候删除这个值会发生什么?
所以当你写的时候:
match self.t.get(&v) {
None => false,
//r: &u16
Some(r) => self.remove(r)
}
捕获的r
类型为&u16
,生命周期为self.t
,即借用。因此,您无法获得调用 remove.
所需的对 self
的可变引用
您的问题最简单的解决方案是 clone()
解决每个生命周期问题 模式。由于您的值是 u16
类型,即 Copy
,它实际上是微不足道的:
match self.t.get(&v) {
None => false,
//r: u16
Some(&r) => self.remove(&r)
}
现在 r
实际上是 u16
类型,所以它没有借用任何东西,你可以随意改变 self
。
如果您的 key/value 类型不是 Copy
,您可以尝试 clone
它们,如果您愿意为此付费。如果没有,还有另一种选择,因为您的 remove()
函数不会修改 HashMap
,而是修改不相关的 HashSet
。如果你注意不要重新借用,你仍然可以改变那个集合 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)
}
}
似乎以下解决方案适用于像这里的 u16 这样的原始类型。对于其他类型,所有权已移动。
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());
}
对于其他类型,我们必须克隆值:
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);
}
但我想要一个没有分配的解决方案
我可以理解 borrowing/ownership 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());
}
编译器出现错误:
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
我不明白为什么我之前做get
(读取值)时不能在match arm中变异;当通过 remove
的突变开始时,self.t.get
完成。
这是由于 get
返回的结果 (Option<&u16>
) 的范围造成的吗?结果的生命周期确实在匹配表达式内有一个范围,但这种设计模式经常使用(在匹配表达式中变异)。
如何解决该错误?
您正在尝试通过使用您获得的 value
而不是 key
从 HashMap
中删除值。
只更改了第 26 行 Some(_) => self.remove(&v)
这会起作用:
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());
}
函数HashMap::<K,V>::get()
的声明有点简化:
pub fn get<'s>(&'s self, k: &K) -> Option<&'s V>
这意味着它 returns 对包含值的可选引用,而不是值本身。由于返回的引用指向地图内部的一个值,它实际上是借用了地图,也就是说,当这个引用存在时你不能改变地图。这个限制是为了保护你,如果你在引用还活着的时候删除这个值会发生什么?
所以当你写的时候:
match self.t.get(&v) {
None => false,
//r: &u16
Some(r) => self.remove(r)
}
捕获的r
类型为&u16
,生命周期为self.t
,即借用。因此,您无法获得调用 remove.
self
的可变引用
您的问题最简单的解决方案是 clone()
解决每个生命周期问题 模式。由于您的值是 u16
类型,即 Copy
,它实际上是微不足道的:
match self.t.get(&v) {
None => false,
//r: u16
Some(&r) => self.remove(&r)
}
现在 r
实际上是 u16
类型,所以它没有借用任何东西,你可以随意改变 self
。
如果您的 key/value 类型不是 Copy
,您可以尝试 clone
它们,如果您愿意为此付费。如果没有,还有另一种选择,因为您的 remove()
函数不会修改 HashMap
,而是修改不相关的 HashSet
。如果你注意不要重新借用,你仍然可以改变那个集合 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)
}
}
似乎以下解决方案适用于像这里的 u16 这样的原始类型。对于其他类型,所有权已移动。
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());
}
对于其他类型,我们必须克隆值:
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);
}
但我想要一个没有分配的解决方案