【问题标题】:Difference between Iterating over BTreeMap and &BTreeMap迭代 BTreeMap 和 &BTreeMap 的区别
【发布时间】:2019-10-13 03:21:00
【问题描述】:

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

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

有人能解释一下迭代&amp;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);
}

【问题讨论】:

  • 实际区别在于(&amp;k, &amp;v)(k, v)。两个 sn-ps 都可以与 rows.push((k, v)) 一起使用,但都不能与 (&amp;k, &amp;v) 一起使用。
  • 简短回答:当迭代 &amp;my_btree 时,值是借用的,因此您会获得与集合相关的生命周期的引用。但是当迭代my_btree 时,你会得到拥有的值。如果您创建它们的引用,它们只会在 kv 超出范围之前有效。
  • @Zefick:感谢您的回答,但我最后不得不引用(请参阅底部的代码块),这就是为什么我有 (&k, &v) 并且主要是来说明为什么它们彼此不同。

标签: rust ownership


【解决方案1】:

其中的微妙之处在于 IntoIteratorBTreeMap 的双重实现(实际上是大多数集合)。你可以在documentation看到,按顺序:

  • impl&lt;K, V&gt; IntoIterator for BTreeMap&lt;K, V&gt;

这是您的第一个案例。您正在做的是移动 BTreeMap 并使用它来生成 (K, V) 迭代器。您可以通过以下 sn-p 说服自己:

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。在您的 sn-p 中,您有 &amp;k&amp;v - 这些引用永远不会起作用,因为这些项目会立即从范围中删除。

  • impl&lt;'a, K, V&gt; IntoIterator for &amp;'a BTreeMap&lt;K, V&gt;

这是您的第二个案例。在这种情况下,您的迭代器现在是(&amp;'a K, &amp;'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 对拥有的结构进行操作,而在另一种情况下,您正在处理引用。

因此,您的示例函数 - 由于 &amp;ss.x 迭代器从不消耗地图,您实际上从未取消引用地图。

【讨论】:

  • 非常感谢您的详细解答!这真的很有帮助。我错过了一个事实,即有两个不同的IntoIterator's
  • @Nick 实际上有三个,但第三个很明显 (&amp;'a mut BTreeMap),与这个问题没有直接关系。
猜你喜欢
  • 2016-06-01
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 2015-12-31
  • 2023-03-16
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多