我正在尝试迭代哈希图,并根据每个条目的内容,对“不同”条目进行更改。重要的是我永远不要更改我正在查看的条目。我认为这使得算法安全,但我似乎无法说服 Rust 相信这一事实。
HashMap::get_mut
的文档说“返回对与键对应的
value的可变引用”,但错误消息表明它正在尝试锁定哈希图本身:
type Hash = String;
type Hashes = Vec<Hash>;
...
struct RawNode
{ parents : Hashes,
children : Hashes,
...
fn make_bi() -> HashMap<Hash, RawNode> {
let raw_graph = make_raw(); // Reads stdin to make a map of commits with parents known.
// We want to populate the children of each commit.
for (h, n) in raw_graph.iter() {
for ph in n.parents.iter() {
if let Some(p) = raw_graph.get_mut(ph) {
p.children.push(h.to_string());
}
}
}
raw_graph
}
|| cannot borrow `raw_graph` as mutable because it is also borrowed as immutable
|| |
|| 77 | for (h, n) in raw_graph.iter() {
|| | ----------------
|| | |
|| | immutable borrow occurs here
|| | immutable borrow later used here
|| 78 | for ph in n.parents.iter() {
|| 79 | if let Some(p) = raw_graph.get_mut(ph) {
|| | ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
||
也许这意味着第一个不可变的借用捕获了其网络中的所有条目 - 文档没有指定,但肯定这不是迭代集合所必需的。当迭代实际上返回一个特定条目时,显然该条目会被锁定,但没有理由同时锁定其他条目。
我看到这个问题:
Rust:修改 HashMap 中的值,同时不可变地借用整个 HashMap但是该作者试图同时阅读和编写给定的条目,所以我怀疑那里的建议对于我的目的来说是多余的。它还涉及昂贵的克隆,就我而言,我认为没有必要。
那么我误解了什么?我该如何绕过它而不造成丑陋或浪费?
实际上是这样的:虽然现在这是可能的(尽管不是微不足道的),但直到最近,Rust 的类型系统还无法表达“借用迭代”,即项目仅在循环体期间有效的迭代。相反,Rust 的迭代要求迭代器在整个迭代及之后都有效,例如
let mut it = raw_graph.iter();
let i1 = it.next();
let i2 = it.next();
let i3 = it.next();
必须是合法的。
请注意,这与本例无关。
当迭代实际返回一个特定条目时,显然该条目会被锁定,但没有理由同时锁定其他条目。
您正在动态推理,但借用检查器是静态的,它无法知道
ph
的条目在所有情况下都与
n
不同(即使是这种情况)。因此,获得对值的可变引用需要获得对集合的可变引用以确保不会出现别名,因此
HashMap::get_mut
的签名在主题上是明确的:fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
您可以通过内部可变性
进行“动态借用检查”,例如在你的值周围加上 RefCell
可以让你通过共享引用改变底层值(运行时检查后)。
dashmap
允许从共享引用获取可变子引用,但如果同一线程持有对同一
shard的未完成引用(用于管理并发访问的内部块),则会发生死锁。