具有迭代器绑定的通用特征的生命周期不匹配
Lifetime mismatch in generic trait with iterator bound
我正在尝试将接口抽象为映射类型的支持数据结构(当前为 std::collections::HashMap
和 std::collections::BTreeMap
),以便我可以在不影响调用代码的情况下换出数据结构。我 运行 因一种特定方法而陷入终身错误:(playground)
use std::collections::hash_map::{HashMap, Keys};
pub trait GroupedCollection<K, V, KeyIterator>
where
KeyIterator: Iterator
{
fn keys(&self) -> KeyIterator;
}
impl<K, V> GroupedCollection<K, V, Keys<'_, K, Vec<V>>> for HashMap<K, Vec<V>>
{
fn keys(&self) -> Keys<'_, K, Vec<V>> {
HashMap::keys(&self)
}
}
fn main() {}
编译器告诉我生命周期不匹配:
error: `impl` item signature doesn't match `trait` item signature
--> src/main.rs:12:5
|
7 | fn keys(&self) -> KeyIterator;
| ------------------------------ expected `fn(&'1 HashMap<K, Vec<V>>) -> std::collections::hash_map::Keys<'2, K, Vec<V>>`
...
12 | fn keys(&self) -> Keys<'_, K, Vec<V>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 HashMap<K, Vec<V>>) -> std::collections::hash_map::Keys<'1, K, Vec<V>>`
|
= note: expected `fn(&'1 HashMap<K, Vec<V>>) -> std::collections::hash_map::Keys<'2, K, Vec<V>>`
found `fn(&'1 HashMap<K, Vec<V>>) -> std::collections::hash_map::Keys<'1, K, Vec<V>>`
help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
--> src/main.rs:7:23
|
7 | fn keys(&self) -> KeyIterator;
| ^^^^^^^^^^^ consider borrowing this type parameter in the trait
我对预留生命周期的工作不多'_
。通过阅读 The Rust Reference: Lifetime Elision,如果我理解正确(但我不确定我是否理解正确),则适用以下内容:
Default Trait Object Lifetimes
...
If the trait object is used as a type argument of a generic type then the containing type is first used to try to infer a bound.
- If there is a unique bound from the containing type then that is the default
...
我的理解是,这是编译器在 impl
签名上为两个生命周期参数派生 '1
的地方。
由于我没有在特征定义中向编译器提供任何关于生命周期的信息,我的理解是编译器假定 fn keys()
中的 &self
和 type Item = &'a K
单态化 hash_map::Keys
将具有不同的生命周期。
看完, I think I might have arrived at the right solution, but it's melting my brain a bit: (playground)
use std::collections::hash_map::{HashMap, Keys};
pub trait GroupedCollection<'a, K: 'a, V, KeyIterator>
where
KeyIterator: Iterator<Item = &'a K>,
{
fn keys(&'a self) -> KeyIterator;
}
impl<'a, K, V> GroupedCollection<'a, K, V, Keys<'a, K, Vec<V>>> for HashMap<K, Vec<V>>
{
fn keys(&'a self) -> Keys<K, Vec<V>> {
HashMap::keys(&self)
}
}
我对问题的理解是否正确?在此处添加显式生命周期 'a
是正确的解决方案,还是我做了一些碰巧编译的愚蠢事情?
写一生'_
的意思是:“这里我应该写一生,但我不是特别在意,所以……随便吧”。
特别是,当你将它写在一个 trait impl 中时,例如:
impl<K, V> GroupedCollection<K, V, Keys<'_, K, Vec<V>>> for ...
就好像你写了:
impl<'x, K, V> GroupedCollection<K, V, Keys<'x, K, Vec<V>>> for ...
也就是说,这个 impl 的用户可以用他们想要的任何生命周期来实例化它。
但是当你把它写成return类型的函数时比如:
fn keys(&self) -> Keys<'_, K, Vec<V>>
就像你写的一样:
fn keys<'s>(&'s self) -> Keys<'s, K, Vec<V>>
即按照默认的生命周期规则推导出生命周期。
现在,你的第一个编译器错误发生了,因为你的 trait 参数的 Keys<'x, _, _>
和你的 Keys<'s, _, _>
不一样,所以函数的类型 keys()
不匹配特征定义。
如何解决?那么,您的 keys
实现必须 return 一个生命周期限制为 self
的值,因此如果您愿意更改特征以反映这一点:
pub trait GroupedCollection<'a, K, V, KeyIterator: 'a> {
fn keys(&'a self) -> KeyIterator;
}
impl<'a, K, V> GroupedCollection<'a, K, V, Keys<'a, K, Vec<V>>> for HashMap<K, Vec<V>>
{
fn keys(&'a self) -> Keys<'a, K, Vec<V>> {
HashMap::keys(&self)
}
}
这几乎就是您在第二个版本中所写的内容(我认为您添加了一些不需要的约束)。
根据上面@cdhowie 的评论,您真正想要的是通用关联类型 (GAT)。这意味着决定生命周期的是特征的实现,而不是用户。不幸的是,它们仍然不稳定:
#![feature(generic_associated_types)]
use std::collections::hash_map::{HashMap, Keys};
pub trait GroupedCollection<K, V>
{
type KeyIterator<'s> where Self: 's;
fn keys<'s>(&'s self) -> Self::KeyIterator<'s>;
}
impl<K, V> GroupedCollection<K, V> for HashMap<K, Vec<V>>
{
type KeyIterator<'s> = Keys<'s, K, Vec<V>>
where Self: 's;
fn keys<'s>(&'s self) -> Keys<'s, K, Vec<V>> {
HashMap::keys(&self)
}
}
我认为它更容易理解和使用。
我正在尝试将接口抽象为映射类型的支持数据结构(当前为 std::collections::HashMap
和 std::collections::BTreeMap
),以便我可以在不影响调用代码的情况下换出数据结构。我 运行 因一种特定方法而陷入终身错误:(playground)
use std::collections::hash_map::{HashMap, Keys};
pub trait GroupedCollection<K, V, KeyIterator>
where
KeyIterator: Iterator
{
fn keys(&self) -> KeyIterator;
}
impl<K, V> GroupedCollection<K, V, Keys<'_, K, Vec<V>>> for HashMap<K, Vec<V>>
{
fn keys(&self) -> Keys<'_, K, Vec<V>> {
HashMap::keys(&self)
}
}
fn main() {}
编译器告诉我生命周期不匹配:
error: `impl` item signature doesn't match `trait` item signature
--> src/main.rs:12:5
|
7 | fn keys(&self) -> KeyIterator;
| ------------------------------ expected `fn(&'1 HashMap<K, Vec<V>>) -> std::collections::hash_map::Keys<'2, K, Vec<V>>`
...
12 | fn keys(&self) -> Keys<'_, K, Vec<V>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 HashMap<K, Vec<V>>) -> std::collections::hash_map::Keys<'1, K, Vec<V>>`
|
= note: expected `fn(&'1 HashMap<K, Vec<V>>) -> std::collections::hash_map::Keys<'2, K, Vec<V>>`
found `fn(&'1 HashMap<K, Vec<V>>) -> std::collections::hash_map::Keys<'1, K, Vec<V>>`
help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
--> src/main.rs:7:23
|
7 | fn keys(&self) -> KeyIterator;
| ^^^^^^^^^^^ consider borrowing this type parameter in the trait
我对预留生命周期的工作不多'_
。通过阅读 The Rust Reference: Lifetime Elision,如果我理解正确(但我不确定我是否理解正确),则适用以下内容:
Default Trait Object Lifetimes
...
If the trait object is used as a type argument of a generic type then the containing type is first used to try to infer a bound.
- If there is a unique bound from the containing type then that is the default
...
我的理解是,这是编译器在 impl
签名上为两个生命周期参数派生 '1
的地方。
由于我没有在特征定义中向编译器提供任何关于生命周期的信息,我的理解是编译器假定 fn keys()
中的 &self
和 type Item = &'a K
单态化 hash_map::Keys
将具有不同的生命周期。
看完
use std::collections::hash_map::{HashMap, Keys};
pub trait GroupedCollection<'a, K: 'a, V, KeyIterator>
where
KeyIterator: Iterator<Item = &'a K>,
{
fn keys(&'a self) -> KeyIterator;
}
impl<'a, K, V> GroupedCollection<'a, K, V, Keys<'a, K, Vec<V>>> for HashMap<K, Vec<V>>
{
fn keys(&'a self) -> Keys<K, Vec<V>> {
HashMap::keys(&self)
}
}
我对问题的理解是否正确?在此处添加显式生命周期 'a
是正确的解决方案,还是我做了一些碰巧编译的愚蠢事情?
写一生'_
的意思是:“这里我应该写一生,但我不是特别在意,所以……随便吧”。
特别是,当你将它写在一个 trait impl 中时,例如:
impl<K, V> GroupedCollection<K, V, Keys<'_, K, Vec<V>>> for ...
就好像你写了:
impl<'x, K, V> GroupedCollection<K, V, Keys<'x, K, Vec<V>>> for ...
也就是说,这个 impl 的用户可以用他们想要的任何生命周期来实例化它。
但是当你把它写成return类型的函数时比如:
fn keys(&self) -> Keys<'_, K, Vec<V>>
就像你写的一样:
fn keys<'s>(&'s self) -> Keys<'s, K, Vec<V>>
即按照默认的生命周期规则推导出生命周期。
现在,你的第一个编译器错误发生了,因为你的 trait 参数的 Keys<'x, _, _>
和你的 Keys<'s, _, _>
不一样,所以函数的类型 keys()
不匹配特征定义。
如何解决?那么,您的 keys
实现必须 return 一个生命周期限制为 self
的值,因此如果您愿意更改特征以反映这一点:
pub trait GroupedCollection<'a, K, V, KeyIterator: 'a> {
fn keys(&'a self) -> KeyIterator;
}
impl<'a, K, V> GroupedCollection<'a, K, V, Keys<'a, K, Vec<V>>> for HashMap<K, Vec<V>>
{
fn keys(&'a self) -> Keys<'a, K, Vec<V>> {
HashMap::keys(&self)
}
}
这几乎就是您在第二个版本中所写的内容(我认为您添加了一些不需要的约束)。
根据上面@cdhowie 的评论,您真正想要的是通用关联类型 (GAT)。这意味着决定生命周期的是特征的实现,而不是用户。不幸的是,它们仍然不稳定:
#![feature(generic_associated_types)]
use std::collections::hash_map::{HashMap, Keys};
pub trait GroupedCollection<K, V>
{
type KeyIterator<'s> where Self: 's;
fn keys<'s>(&'s self) -> Self::KeyIterator<'s>;
}
impl<K, V> GroupedCollection<K, V> for HashMap<K, Vec<V>>
{
type KeyIterator<'s> = Keys<'s, K, Vec<V>>
where Self: 's;
fn keys<'s>(&'s self) -> Keys<'s, K, Vec<V>> {
HashMap::keys(&self)
}
}
我认为它更容易理解和使用。