遍历BTreeMap和&BTreeMap之间的区别

时间:2019-10-12 18:50:46

标签: rust ownership

我试图了解两者之间的区别

let rows = Vec::new();
for (k, v) in my_btree { // BTreeMap<i64, String>
    rows.push((&k, &v)) // k and v don't live long enough.
}

和:

let rows = Vec::new();
for (k, v) in &my_btree { // BTreeMap<i64, String>
    rows.push((k, v))
}

有人可以解释在&my_btree上进行迭代与在my_btree上进行迭代之间的区别吗?

具体来说,我想了解上述两个示例中所有权的变化以及所引用的内存

这是我要执行的操作的完整示例(target_function是我正在使用的库,它具有此处的函数签名,因此不能更改):

use std::collections::BTreeMap;

struct SomeStruct {
    x: BTreeMap<i64, String>
}

fn target_function(rows: &[(&i64, &String)]) {
    for row in rows.iter() {
        println!("{:#?}", row);
    }    
}
fn test(ss: SomeStruct) {
    let mut rows = Vec::new();

    for (k, v) in &ss.x {
        rows.push((k, v));
    }
    target_function(&rows[..]);
}

fn main() {
    let mut a = BTreeMap::new();
    a.insert(1, "hello".to_string());
    a.insert(2, "goodbye".to_string());

    let mystruct = SomeStruct{x: a};
    test(mystruct);
}

1 个答案:

答案 0 :(得分:1)

对于IntoIterator(实际上是大多数集合)的双重实现是BTreeMap。您可以在documentation中按顺序查看它:

  • impl<K, V> IntoIterator for BTreeMap<K, V>

这是您的第一种情况。您正在做的是移动BTreeMap并使用它来生成(K, V)迭代器。您可以使用以下代码片段使自己相信这一点:

let mut my_btree:BTreeMap<i64, String> = BTreeMap::new();
my_tree.insert(3, "this is a test".to_string());
let mut rows = Vec::new();
for (k, v) in my_btree { // BTreeMap<i64, String>
    rows.push((k, v))
}

这消耗my_btree并一对一地产生(K, V)对。由于这些对是所有的,因此您可以安全地将它们放入您提供的Vec中。在您的代码段中,您有&k&v-这些引用将永远无法正常工作,因为这些项目会立即从范围中删除。

  • impl<'a, K, V> IntoIterator for &'a BTreeMap<K, V>

这是您的第二种情况。在这种情况下,您的迭代器现在为(&'a K, &'a V),您可以通过尝试将Vec移出BTreeMap的范围来轻松地说服自己,就像这样:

fn does_not_work<'a>() -> Vec<(&'a i64, &'a String)> {
    let my_btree:BTreeMap<i64, String> = BTreeMap::new();
    let mut rows = Vec::new();
    for (k, v) in &my_btree { // BTreeMap<i64, String>
        rows.push((k, v))
    }
    rows
}

这不会编译,因为您在BTreeMap中插入了对元素的一堆引用,然后将其删除(由于它移出了范围)-如果借用检查器没有将所有这些引用都无效来救援吧。

这就是区别-在一种情况下,您正在消耗BTreeMap来对拥有的结构进行操作,而在另一种情况下,您正在处理引用。

由于这个原因,您的示例函数-由于&ss.x迭代器从未使用过映射,因此您实际上从未取消引用该映射。