我正在尝试使用GATs将API增强到内存中的数据存储区。数据是以值的形式组织的,其中每个值都包含一个查找键。您可以将其看作是数据库表中的一行,其中整个行都是“值”,但也包含一个或多个主键列。
这个想法是用一个特征来描述这一点,这样你就可以通过提供密钥来寻找一个特定的价值。键必须能够引用值,这样如果值的键-部分是String,您可以只使用&str来查找它。这是GATs输入图片的地方:
pub trait Value {
type Key<'a>: PartialEq where Self: 'a;
fn as_key<'a>(&'a self) -> Self::Key<'a>;
}Key<'a> GAT提供了一个生命周期,as_key()可以用来返回引用内部数据的值。请注意,as_key()不能仅仅返回对键的引用,因为返回的键可以是在Self中不逐字存在的东西,例如复合键。例如,这些都是可能的:
struct Data {
s: String,
n: u64,
// ... more fields ...
}
// example 1: expose key as self.s as a &str key
impl Value for Data {
type Key<'a> = &'a str;
fn as_key(&self) -> &str { &self.s }
}
// example 2: expose key as a pair of (self.s.as_str(), self.n)
impl Value for Data {
type Key<'a> = (&'a str, u64);
fn as_key(&self) -> (&str, u64) { (&self.s, self.n) }
}使用此特性的泛型代码示例如下所示:
pub struct Table<T> {
data: Vec<T>,
}
impl<T: Value> Table<T> {
fn find<'a: 'k, 'k>(&'a self, k: T::Key<'k>) -> Option<usize> {
self.data.iter().position(|v| v.as_key() == k)
}
}这是很好的工作,你可以玩它在操场上。(一个更现实的例子将需要Ord或Hash从Value::Key构建一个更复杂的存储,但这足以说明这个想法。)
现在,让我们做一个简单的更改,并将表数据存储在Mutex中。代码看起来几乎相同,而且由于它只返回位置,互斥操作应该保持在实现的内部:
struct Table<T> {
data: Mutex<Vec<T>>,
}
impl<T: Value> Table<T> {
pub fn find<'a: 'k, 'k>(&'a self, k: T::Key<'k>) -> Option<usize> {
let data = self.data.lock().unwrap();
data.iter().position(|v| v.as_key() == k)
}
}然而,上面的代码并没有编译--它抱怨“数据寿命不够长”:
error[E0597]: `data` does not live long enough
--> src/main.rs:18:9
|
16 | pub fn find<'a: 'k, 'k>(&'a self, k: T::Key<'k>) -> Option<usize> {
| -- lifetime `'k` defined here
17 | let data = self.data.lock().unwrap();
18 | data.iter().position(|v| v.as_key() == k)
| ^^^^^^^^^^^ ---------- argument requires that `data` is borrowed for `'k`
| |
| borrowed value does not live long enough
19 | }
| - `data` dropped here while still borrowed我不太明白这个错误--为什么data需要在我们所比较的键的生命周期中生存呢?我试过:
'k和'a的寿命完全解耦&'a T和&T::Key<'b>,并在比较它们之后返回一个bool (它自己编译)Iterator::position()循环替换for但是没有任何帮助,错误总是以某种形式存在。请注意,将v.as_key()和k放在同一个闭包(例如像这样)中是完全合法的,只有当您试图比较它们时,才会出现错误。
我对这个问题的直观理解是,与Value::Key<'a>关联的Value::Key<'a>绑定只适用于另一个Key<'a>。
是否有可能对生命周期或as_key()接口进行重新工作来解决这个问题?这是这里描述的问题的变体吗?
编辑:按照kmdreko的建议,放松绑定到HRTB for<'b> PartialEq<Self::Key<'b>>的PartialEq,修复上面的示例,但是使用泛型中断。例如,Value的这个实现无法编译:
struct NoKey<T>(T);
impl<T> Value for NoKey<T> {
type Key<'a> = () where T: 'a;
fn as_key(&self) -> () {
()
}
}有错误:
error[E0311]: the parameter type `T` may not live long enough
--> src/lib.rs:38:20
|
38 | type Key<'a> = () where T: 'a;
| ^^ ...so that the type `NoKey<T>` will meet its required lifetime bounds发布于 2022-11-13 00:13:37
我对这个问题的直观理解是,与
Value::Key<'a>关联的Value::Key<'a>绑定只适用于另一个Key<'a>。
这是正确的。您可以通过使用一个级别更高的特征来放松这个约束,它要求Key<'a>可以与所有Key<'b>进行比较。
pub trait Value {
type Key<'a>: for<'b> PartialEq<Self::Key<'b>> // <-----
where
Self: 'a;
fn as_key<'a>(&'a self) -> Self::Key<'a>;
}我不认为有任何其他方法,因为大多数类型在关联寿命方面都是协变的,但是使用T::Key<'k>,我认为您不能限制'k可以缩短。
在编辑的问题中指出的泛型问题可以通过要求泛型为'static (游乐场)来解决。请注意,'static绑定仅适用于整个值,键仍可能引用值的部分。
https://stackoverflow.com/questions/74417255
复制相似问题