Getting first member of a BTreeSet

萝らか妹 提交于 2021-02-05 07:31:18

问题


In Rust, I have a BTreeSet that I'm using to keep my values in order. I have a loop that should retrieve and remove the first (lowest) member of the set. I'm using a cloned iterator to retrieve the first member. Here's the code:

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);

    }
}

This, however, gives me the following compile error:

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

Why is start_nodes.iter() considered an immutable borrow? What approach should I take instead to get the first member?

I'm using version 1.14.0 (not by choice).


回答1:


Why is start_nodes.iter() considered an immutable borrow?

Whenever you ask a question like this one, you need to look at the prototype of the function, in this case the prototype of BTreeSet::iter():

fn iter(&self) -> Iter<T>

If we look up the Iter type that is returned, we find that it's defined as

pub struct Iter<'a, T> where T: 'a { /* fields omitted */ }

The lifetime 'a is not explicitly mentioned in the definition of iter(); however, the lifetime elision rules make the function definition equivalent to

fn iter<'a>(&'a self) -> Iter<'a, T>

From this expanded version, you can see that the return value has a lifetime that is bound to the lifetime of the reference to self that you pass in, which is just another way of stating that the function call creates a shared borrow that lives as long as the return value. If you store the return value in a variable, the borrow lives at least as long as the variable.

What approach should I take instead to get the first member?

As noted in the comments, your code works on recent versions of Rust due to non-lexical lifetimes – the compiler figures out by itself that start_iter and start_iter_cloned don't need to live longer than the call to next(). In older versions of Rust, you can artificially limit the lifetime by introducing a new scope:

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);
}

However, note that this code is needlessly long-winded. The new iterator you create and its cloning version only live inside the new scope, and they aren't really used for any other purpose, so you could just as well write

while !start_nodes.is_empty() {
    let n = start_nodes.iter().next().unwrap().clone();
    start_nodes.remove(&n);
}

which does exactly the same, and avoids the issues with long-living borrows by avoiding to store the intermediate values in variables, to ensure their lifetime ends immediately after the expression.

Finally, while you don't give full details of your use case, I strongly suspect that you would be better off with a BinaryHeap instead of a 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`
    }
}

This code is shorter, simpler, completely sidesteps the issue with the borrow checker, and will also be more efficient.




回答2:


Not sure this is the best approach, but I fixed it by introducing a new scope to ensure that the immutable borrow ends before the mutable borrow occurs:

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);    
    } 
}


来源:https://stackoverflow.com/questions/54507150/getting-first-member-of-a-btreeset

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!