想要使用模式匹配添加到 HashMap,一次多次借用可变的
Want to add to HashMap using pattern match, get borrow mutable more than once at a time
我正在尝试编写一些玩具代码来存储它在 HashMap
中看到某个单词的次数。如果该键存在,它会将计数器加一,如果该键不存在,它会将值 1
添加。我本能地想通过模式匹配来做到这一点,但我不止一次遇到了借用可变错误:
fn read_file(name: &str) -> io::Result<HashMap<String, i32>> {
let b = BufReader::new(File::open(name)?);
let mut c = HashMap::new();
for line in b.lines() {
let line = line?;
for word in line.split(" ") {
match c.get_mut(word) {
Some(i) => {
*i += 1;
},
None => {
c.insert(word.to_string(), 1);
}
}
}
}
Ok(c)
}
我得到的错误是:
error[E0499]: cannot borrow `c` as mutable more than once at a time
--> <anon>:21:21
|
16 | match c.get_mut(word) {
| - first mutable borrow occurs here
...
21 | c.insert(word.to_string(), 1);
| ^ second mutable borrow occurs here
22 | }
23 | }
| - first borrow ends here
我理解为什么编译器脾气暴躁:我已经告诉它我要改变 word
上键入的值,但是插入不在那个值上。然而,插入是在 None
上,所以我认为编译器可能已经意识到现在没有机会改变 c[s]
。
我觉得这个方法应该有效,但我错过了一个窍门。我做错了什么?
编辑:我意识到我可以使用
来做到这一点
if c.contains_key(word) {
if let Some(i) = c.get_mut(s) {
*i += 1;
}
} else {
c.insert(word.to_string(), 1);
}
但这看起来非常丑陋的代码与模式匹配(特别是必须做 contains_key()
检查,然后使用 Some
.
再次检查
您必须使用条目 "pattern":
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
fn main() {
let mut words = vec!["word1".to_string(), "word2".to_string(), "word1".to_string(), "word3".to_string()];
let mut wordCount = HashMap::<String, u32>::new();
for w in words {
let val = match wordCount.entry(w) {
Vacant(entry) => entry.insert(0),
Occupied(entry) => entry.into_mut(),
};
// do stuff with the value
*val += 1;
}
for k in wordCount.iter() {
println!("{:?}", k);
}
}
Entry 对象允许您在缺少值时插入值,或者在值已存在时修改它。
https://doc.rust-lang.org/stable/std/collections/hash_map/enum.Entry.html
HashMap::entry()
is the method to use here. In most cases you want to use with Entry::or_insert()
插入一个值:
for word in line.split(" ") {
*c.entry(word).or_insert(0) += 1;
}
如果要插入的值需要进行昂贵的计算,可以使用Entry::or_insert_with()
to make sure the computation is only executed when it needs to. Both or_insert
methods will probably cover all of your needs. But if you, for whatever reason, want to do something else, you can still simply match
on the Entry
枚举。
这基本上不再是问题了。 和non-lexical lifetimes (NLL), your code compiles without problems. Your example on the Playground.
NLL 是编译器推理借用的一种新方法。 NLL 已在 Rust 2018 (≥ 1.31) 中启用。它最终也计划在 Rust 2015 中启用。您可以在 this official blog post.
中阅读有关 NLL 和版本的更多信息
在这种特殊情况下,我仍然认为 (entry(word).or_insert(0)
) 是最好的解决方案,只是因为它非常简洁。
我正在尝试编写一些玩具代码来存储它在 HashMap
中看到某个单词的次数。如果该键存在,它会将计数器加一,如果该键不存在,它会将值 1
添加。我本能地想通过模式匹配来做到这一点,但我不止一次遇到了借用可变错误:
fn read_file(name: &str) -> io::Result<HashMap<String, i32>> {
let b = BufReader::new(File::open(name)?);
let mut c = HashMap::new();
for line in b.lines() {
let line = line?;
for word in line.split(" ") {
match c.get_mut(word) {
Some(i) => {
*i += 1;
},
None => {
c.insert(word.to_string(), 1);
}
}
}
}
Ok(c)
}
我得到的错误是:
error[E0499]: cannot borrow `c` as mutable more than once at a time
--> <anon>:21:21
|
16 | match c.get_mut(word) {
| - first mutable borrow occurs here
...
21 | c.insert(word.to_string(), 1);
| ^ second mutable borrow occurs here
22 | }
23 | }
| - first borrow ends here
我理解为什么编译器脾气暴躁:我已经告诉它我要改变 word
上键入的值,但是插入不在那个值上。然而,插入是在 None
上,所以我认为编译器可能已经意识到现在没有机会改变 c[s]
。
我觉得这个方法应该有效,但我错过了一个窍门。我做错了什么?
编辑:我意识到我可以使用
来做到这一点 if c.contains_key(word) {
if let Some(i) = c.get_mut(s) {
*i += 1;
}
} else {
c.insert(word.to_string(), 1);
}
但这看起来非常丑陋的代码与模式匹配(特别是必须做 contains_key()
检查,然后使用 Some
.
您必须使用条目 "pattern":
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
fn main() {
let mut words = vec!["word1".to_string(), "word2".to_string(), "word1".to_string(), "word3".to_string()];
let mut wordCount = HashMap::<String, u32>::new();
for w in words {
let val = match wordCount.entry(w) {
Vacant(entry) => entry.insert(0),
Occupied(entry) => entry.into_mut(),
};
// do stuff with the value
*val += 1;
}
for k in wordCount.iter() {
println!("{:?}", k);
}
}
Entry 对象允许您在缺少值时插入值,或者在值已存在时修改它。
https://doc.rust-lang.org/stable/std/collections/hash_map/enum.Entry.html
HashMap::entry()
is the method to use here. In most cases you want to use with Entry::or_insert()
插入一个值:
for word in line.split(" ") {
*c.entry(word).or_insert(0) += 1;
}
如果要插入的值需要进行昂贵的计算,可以使用Entry::or_insert_with()
to make sure the computation is only executed when it needs to. Both or_insert
methods will probably cover all of your needs. But if you, for whatever reason, want to do something else, you can still simply match
on the Entry
枚举。
这基本上不再是问题了。 和non-lexical lifetimes (NLL), your code compiles without problems. Your example on the Playground.
NLL 是编译器推理借用的一种新方法。 NLL 已在 Rust 2018 (≥ 1.31) 中启用。它最终也计划在 Rust 2015 中启用。您可以在 this official blog post.
中阅读有关 NLL 和版本的更多信息在这种特殊情况下,我仍然认为 entry(word).or_insert(0)
) 是最好的解决方案,只是因为它非常简洁。