Разница между итерациями по BTreeMap и & BTreeMap - PullRequest
1 голос
/ 12 октября 2019

Я пытаюсь понять разницу между

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 Ответ

1 голос
/ 12 октября 2019

Тонкость в этом заключается с двойной реализацией IntoIterator для BTreeMap (и большинства коллекций на самом деле). Вы можете увидеть это в документации , в следующем порядке:

  • 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, который никогда не использует карту.

...