Я пишу черту как интерфейс для структур данных типа карты (например, 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
для ссылок на итераторы, но, похоже, это не решает проблему: детская площадка)
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.