在Rust中,我有一个BTreeSet
,用于保持我的值井井有条。我有一个循环,应该检索并删除该集合的第一个(最低)成员。我正在使用克隆的迭代器来检索第一个成员。这是代码:
use std::collections::BTreeSet;
fn main() {
let mut start_nodes = BTreeSet::new();
// add items to the set
while !start_nodes.is_empty() {
let mut start_iter = start_nodes.iter();
let mut start_iter_cloned = start_iter.cloned();
let n = start_iter_cloned.next().unwrap();
start_nodes.remove(&n);
}
}
但是,这给了我以下编译错误:
error[E0502]: cannot borrow `start_nodes` as mutable because it is also borrowed as immutable
--> prog.rs:60:6
|
56 | let mut start_iter = start_nodes.iter();
| ----------- immutable borrow occurs here
...
60 | start_nodes.remove(&n);
| ^^^^^^^^^^^ mutable borrow occurs here
...
77 | }
| - immutable borrow ends here
为什么start_nodes.iter()
被认为是不可变的借项?我应该采取什么方法来获得第一个成员?
我使用的是1.14.0
版(不是强制选择的)。
答案 0 :(得分:3)
为什么
start_nodes.iter()
被认为是不可变的借书?
每当您问这样的问题时,都需要查看函数的原型,在这种情况下,请查看BTreeSet::iter()
的原型:
fn iter(&self) -> Iter<T>
如果我们查询返回的Iter
类型,就会发现它被定义为
pub struct Iter<'a, T> where T: 'a { /* fields omitted */ }
'a
的定义中未明确提及生存期iter()
;但是,生存期省略规则使函数定义等同于
fn iter<'a>(&'a self) -> Iter<'a, T>
从该扩展版本中,您可以看到返回值的生存期与您传入的对self
的引用的生存期绑定,这只是说明函数调用创建的另一种方式存续期与回报价值一样长的共享借贷。如果将返回值存储在变量中,则借用寿命至少与变量一样长。
我应该采取什么方法来获得第一个成员?
如注释中所述,由于非词法生存期,您的代码可在Rust的最新版本上工作–编译器自己指出start_iter
和start_iter_cloned
的生存期不必超过呼叫next()
。在旧版的Rust中,您可以通过引入新的作用域来人为地限制生存期:
while !start_nodes.is_empty() {
let n = {
let mut start_iter = start_nodes.iter();
let mut start_iter_cloned = start_iter.cloned();
start_iter_cloned.next().unwrap()
};
start_nodes.remove(&n);
}
但是,请注意,此代码不必要冗长。您创建的新迭代器及其克隆版本仅存在于新作用域内,并且它们并没有真正用于其他目的,因此您也可以编写
while !start_nodes.is_empty() {
let n = start_nodes.iter().next().unwrap().clone();
start_nodes.remove(&n);
}
完全相同,并且通过避免将中间值存储在变量中来确保长寿命借款的问题,从而确保它们的寿命在表达式之后立即结束。
最后,虽然您没有提供用例的完整详细信息,但我强烈怀疑使用BinaryHeap
而不是BTreeSet
会更好:
use std::collections::BinaryHeap;
fn main() {
let mut start_nodes = BinaryHeap::new();
start_nodes.push(42);
while let Some(n) = start_nodes.pop() {
// Do something with `n`
}
}
此代码更短,更简单,完全避开了借阅检查器的问题,并且效率更高。
答案 1 :(得分:0)
不确定这是否是最好的方法,但我通过引入新的作用域来解决它,以确保不可变的借用在发生可变借用之前结束:
use std::collections::BTreeSet;
fn main() {
let mut start_nodes = BTreeSet::new();
// add items to the set
while !start_nodes.is_empty() {
let mut n = 0;
{
let mut start_iter = start_nodes.iter();
let mut start_iter_cloned = start_iter.cloned();
let x = &mut n;
*x = start_iter_cloned.next().unwrap();
}
start_nodes.remove(&n);
}
}