遍历 BTreeMap 和 &BTreeMap 的区别
Difference between Iterating over BTreeMap and &BTreeMap
我正在尝试了解
之间的区别
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);
}
微妙之处在于 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
迭代器从不使用地图,您实际上从未取消引用地图。
我正在尝试了解
之间的区别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);
}
微妙之处在于 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
迭代器从不使用地图,您实际上从未取消引用地图。