我试图了解两者之间的区别
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)
对。由于这些对是own,因此您可以安全地将它们推入您提供的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
迭代器,因此您实际上从未取消引用该映射。