我试图了解两者之间的区别
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);
}
答案 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
迭代器从未使用过映射,因此您实际上从未取消引用该映射。