Пожизненная проблема с привязкой общего признака

Я пишу черту как интерфейс для структур данных типа карты (например, std::collections::BTreeMap и std::collections::HashMap). Это продолжение вопрос, который я задал вчера, хотя и стоит отдельно.

У меня проблема на всю жизнь, которую я не могу понять. Я искал ответы во всех своих учебниках, справочнике Rust, StackOverflow и т. д., но не смог понять, что происходит. Я написал почти дюжину вариантов следующего кода на основе предложений из предыдущего вопроса, и я оказался в той же ситуации. Я надеюсь, что кто-то может помочь мне понять, почему gc3() невозможно или что я делаю неправильно. Я знаю, что вполне возможно, что я так долго сунул нос в эту проблему, что упускаю что-то простое, что должно быть очевидным. (детская площадка)

use std::collections::hash_map::{HashMap, Iter};

fn main() {
    gc1(&HashMap::new());
    gc2(&HashMap::new());
    gc3(HashMap::new());
}

// Works
fn gc1<'a>(map: &'a dyn GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>) {
    let _ = map.iter().collect::<Vec<_>>();
}

// Works
fn gc2<'a, M>(map: &'a M)
where
    M: 'a + GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>,
{
    let _ = map.iter().collect::<Vec<_>>();
}

// Compiler error: `map` does not live long enough
fn gc3<'a, M>(map: M)
where
    M: 'a + GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>,
{
    let _ = map.iter().collect::<Vec<_>>();
}

pub trait GroupedCollection<'a, K, V, I: 'a> {
    fn iter(&'a self) -> I;
}

impl<'a, K, V> GroupedCollection<'a, K, V, Iter<'a, K, Vec<V>>> for HashMap<K, Vec<V>>
{
    fn iter(&'a self) -> Iter<'a, K, Vec<V>> {
        HashMap::iter(&self)
    }
}
error[E0597]: `map` does not live long enough
  --> src/main.rs:27:13
   |
23 | fn gc3<'a, M>(map: M)
   |        -- lifetime `'a` defined here
...
27 |     let _ = map.iter().collect::<Vec<_>>();
   |             ^^^^^^^^^^
   |             |
   |             borrowed value does not live long enough
   |             argument requires that `map` is borrowed for `'a`
28 | }
   | - `map` dropped here while still borrowed

Компилятор жалуется на то, что ссылки, созданные map.iter(), удаляются в конце collect(), потому что collect(self) использует итератор? (Я пытался решить эту проблему, назначив 'a: 'b для GroupedCollection и 'b для ссылок на итераторы, но, похоже, это не решает проблему: детская площадка)

Как создавать пользовательские общие типы в Python (50/100 дней Python)
Как создавать пользовательские общие типы в Python (50/100 дней Python)
Помимо встроенных типов, модуль типизации в Python предоставляет возможность определения общих типов, что позволяет вам определять типы, которые могут...
0
0
35
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

TL;DR: Не используйте параметр времени жизни, используйте HRTB: where M: for<'a> GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>.


'a — время жизни, выбранное вызывающим абонентом. Предположим, вызывающий абонент выбирает 'static (в общем, хорошо сверять время жизни с 'static). map.iter() обессахаривается до <M as GroupedCollection<'static, ...>>::iter(&map). <M as GroupedCollection<'static, ...>>::iter() требует &'a self, то есть &'static self. Но map — это локальная переменная, и, следовательно, &map определенно является нет'static. Бум.

Он работает со ссылками, потому что вызывающий абонент не только выбирает 'a, но и должен предоставить ссылку, которая соответствует ему. Если он выберет 'static, ему придется предоставить &'static M, так что все в порядке.

Решение? Вам нужно время жизни, выбранное вызываемый абонент. То есть M реализует GroupedCollection какое-то время жизни 'a выбираю я, а не мой абонент. В Rust это невозможно выразить, но вы можете сказать «M реализует GroupedCollection для времени жизни Любые», и, очевидно, это включает время жизни, которое я выберу. Это Границы черт более высокого ранга: M: for<'a> GroupedCollection<'a, ...>. Так:

fn gc3<M>(map: M)
where
    M: for<'a> GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>,
{
    // ...
}

Детская площадка.

Это идеально? Нет. Могут быть случаи, когда M не реализует GroupedCollection для времени жизни Любые, но реализует для выбранного нами времени жизни. Но вы не можете сделать лучше без GAT.

Другие вопросы по теме