通用特征绑定的生命周期问题
Lifetime issue with generic trait bound
我正在写一个特征作为映射类型数据结构的接口(例如 std::collections::BTreeMap
和 std::collections::HashMap
)。这是 的后续,尽管它是独立的。
我遇到了一个似乎无法理解的终生问题。我在我所有的教科书、Rust 参考、Whosebug 等中搜索了答案,但我一直无法弄清楚发生了什么。根据上一个问题的建议,我已经在以下代码上编写了将近十几个变体,但我最终遇到了同样的情况。 我希望有人能帮助我理解为什么 gc3()
是不可能的或者我做错了什么。 我知道这完全有可能是我对这个问题不以为然很长一段时间以来,我都遗漏了一些应该显而易见的简单内容。 (playground)
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
分配给迭代器引用来解决这个问题,但它似乎并没有解决问题:playground)
TL;DR: 不要使用生命周期参数,使用 HRTB: where M: for<'a> GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>
.
'a
是 caller-chosen 生命周期。假设调用者选择 '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
,所以一切都很好。
解决办法?你想要一个 callee-选择的生命周期。也就是说,M
实施 GroupedCollection
一段时间 'a
我选择,而不是我的调用者。在 Rust 中无法表达这一点,但你可以说“M
implements GroupedCollection
for any lifetime”,显然这包括我将选择的生命周期.这是 Higher-Ranked Trait Bounds:M: for<'a> GroupedCollection<'a, ...>
。所以:
fn gc3<M>(map: M)
where
M: for<'a> GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>,
{
// ...
}
完美吗?不。可能存在 M
在 any 生命周期内不实现 GroupedCollection
的情况,但它会在我们选择的生命周期内实现。但是如果没有 GAT,你就无法做得更好。
我正在写一个特征作为映射类型数据结构的接口(例如 std::collections::BTreeMap
和 std::collections::HashMap
)。这是
我遇到了一个似乎无法理解的终生问题。我在我所有的教科书、Rust 参考、Whosebug 等中搜索了答案,但我一直无法弄清楚发生了什么。根据上一个问题的建议,我已经在以下代码上编写了将近十几个变体,但我最终遇到了同样的情况。 我希望有人能帮助我理解为什么 gc3()
是不可能的或者我做错了什么。 我知道这完全有可能是我对这个问题不以为然很长一段时间以来,我都遗漏了一些应该显而易见的简单内容。 (playground)
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
分配给迭代器引用来解决这个问题,但它似乎并没有解决问题:playground)
TL;DR: 不要使用生命周期参数,使用 HRTB: where M: for<'a> GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>
.
'a
是 caller-chosen 生命周期。假设调用者选择 '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
,所以一切都很好。
解决办法?你想要一个 callee-选择的生命周期。也就是说,M
实施 GroupedCollection
一段时间 'a
我选择,而不是我的调用者。在 Rust 中无法表达这一点,但你可以说“M
implements GroupedCollection
for any lifetime”,显然这包括我将选择的生命周期.这是 Higher-Ranked Trait Bounds:M: for<'a> GroupedCollection<'a, ...>
。所以:
fn gc3<M>(map: M)
where
M: for<'a> GroupedCollection<'a, usize, usize, Iter<'a, usize, Vec<usize>>>,
{
// ...
}
完美吗?不。可能存在 M
在 any 生命周期内不实现 GroupedCollection
的情况,但它会在我们选择的生命周期内实现。但是如果没有 GAT,你就无法做得更好。