如何在 Vec 上更新或插入? [英] How to update-or-insert on a Vec?
问题描述
我正在用 Rust 编写数据结构.它包含键值对的 Vec
.插入结构时,我需要找到一个匹配的键并更新键和值(实际上是一个子指针).代码看起来有点像这样,其中 pivots
是 ref mut
到 Vec
和 Pivot
是只是一个包含两个字段的结构:
I'm writing a data structure in Rust. It contains a Vec
of key-value pairs. When inserting into the structure, I need to find a matching key and update both the key and the value (which is actually a child pointer). The code looks a bit like this, where pivots
is a ref mut
to Vec<Pivot>
and Pivot
is just a struct with two fields:
match pivots.iter_mut().find(|ref p| key <= p.min_key) { // first mutable borrow
Some(ref mut pivot) => {
// If there is one, insert into it and update the pivot key
pivot.min_key = key;
pivot.child.insert(key, value) // recursive call
},
// o/w, insert a new leaf at the end
None => pivots.push(Pivot /* ... */) // second mutable borrow
}
但是有一个问题.即使我没有在 match
的第二个分支中使用可变迭代器,借用检查器抱怨我不能一次多次借用 *pivots
作为可变的时间".
But there's a problem. Even though I don't use the mutable iterator in the second arm of the match
, the borrow checker complains that I "cannot borrow *pivots
as mutable more than once at a time".
这对我来说很有意义,因为第一个借用仍在范围内,即使在 match
的那种情况下没有使用它.这有点不方便:更聪明的检查器当然可以判断借用是非重叠的.我在网上看到有人建议使用提前退货来避免这个问题,就像这样:
This makes perfect sense to me, because the first borrow is still in scope, even though it's not used in that case of the match
. It's a little inconvenient: a cleverer checker could certainly tell that the borrows are non-overlapping. I've seen someone online advising to use early-return to avoid the problem, like this:
match pivots.iter_mut().find(|ref p| key <= p.min_key) {
Some(ref mut pivot) => {
pivot.min_key = key;
pivot.child.insert(key, value);
return
},
None => ()
};
pivots.push(Pivot /* ... */)
但这似乎难以理解,尤其是当这意味着将这段代码分解为自己的函数以允许返回
时.是否有更惯用的方式来执行更新或插入操作?
but this seems hard-to-understand, especially when it means breaking out this code into its own function to allow the return
. Is there a more idiomatic way to perform the update-or-insert operation?
推荐答案
有一个合并的 RFC "非词法生命周期" 从长远来看解决了这个问题.使用 Rust 2018 中的非词法生命周期(在 Rust 1.31 中可用),您的代码按原样运行:
There is a merged RFC "non-lexical lifetimes" which solves this in the long run. Using the non-lexical lifetimes in Rust 2018, available in Rust 1.31, your code works as-is:
use std::collections::HashMap;
pub struct Pivot {
pub min_key: u64,
pub child: HashMap<u64, ()>,
}
fn update_or_append(pivots: &mut Vec<Pivot>, key: u64, value: ()) {
match pivots.iter_mut().find(|ref p| key <= p.min_key) {
Some(pivot) => {
// If there is one, insert into it and update the pivot key
pivot.min_key = key;
pivot.child.insert(key, value);
return;
}
// o/w insert a new leaf at the end
None => {
let mut m = HashMap::new();
m.insert(key, value);
pivots.push(Pivot {
min_key: key,
child: m,
});
}
}
}
fn main() {
let mut pivots = Vec::new();
update_or_append(&mut pivots, 100, ());
}
如果这对您的代码不起作用,请查看
在 Rust 2018 之前,您可以通过一些额外的控制流处理来解决它.
Before Rust 2018, you can workaround it with some additional control flow handling.
无论更新是否发生,您都可以让您的匹配产生一个 bool
值,并在下面使用该值附加一个条件块.我考虑将更新或附加"逻辑放入一个单独的函数中(更新后使用 return
)更惯用的方法:
You could have your match produce a bool
value whether the update happened or not, and have a conditional block below using that value to append. I consider putting the "update-or-append" logic into a separate function (using return
after the update) the more idiomatic approach:
use std::collections::HashMap;
pub struct Pivot {
pub min_key: u64,
pub child: HashMap<u64, ()>,
}
fn update_or_append(pivots: &mut Vec<Pivot>, key: u64, value: ()) {
if let Some(pivot) = pivots.iter_mut().find(|ref p| key <= p.min_key) {
// If there is one, insert into it and update the pivot key
pivot.min_key = key;
pivot.child.insert(key, value);
return;
}
// otherwise insert a new leaf at the end
let mut m = HashMap::new();
m.insert(key, value);
pivots.push(Pivot {
min_key: key,
child: m,
});
}
fn main() {
let mut pivots = Vec::new();
update_or_append(&mut pivots, 100, ());
}
使用 bool
跟踪更新是否发生:
Using a bool
to track whether the update happened:
use std::collections::HashMap;
pub struct Pivot {
pub min_key: u64,
pub child: HashMap<u64, ()>,
}
fn update_or_append(pivots: &mut Vec<Pivot>, key: u64, value: ()) {
let updated = match pivots.iter_mut().find(|ref p| key <= p.min_key) {
Some(pivot) => {
// If there is one, insert into it and update the pivot key
pivot.min_key = key;
pivot.child.insert(key, value);
true
}
// o/w insert a new leaf at the end below
None => false,
};
if !updated {
let mut m = HashMap::new();
m.insert(key, value);
pivots.push(Pivot {
min_key: key,
child: m,
});
}
}
fn main() {
let mut pivots = Vec::new();
update_or_append(&mut pivots, 100, ());
}
这篇关于如何在 Vec 上更新或插入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!