通用特征绑定的终身问题

Dyl*_*n L 1 generics iterator traits rust

我正在编写一个特征作为映射类型数据结构(例如std::collections::BTreeMapstd::collections::HashMap)的接口。这是我昨天提出的问题的后续,尽管它是独立的。

我有一个我似乎无法理解的终生问题。我在我所有的教科书、The Rust Reference、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)
    }
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

编译器是否会抱怨,因为 产生的引用map.iter()在 的末尾被删除,collect()因为collect(self)消耗了迭代器?(我尝试通过分配给迭代器引用来解决这个问题'a: 'bGroupedCollection'b它似乎并没有解决问题:playground

Cha*_*man 6

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>>>,
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

游乐场

完美吗?不会。在某些情况下,可能M不会GroupedCollection任何生命周期内实现,但会在我们选择的生命周期内实现。但如果没有 GAT,你就无法做得更好。