如何定义适配器特征,其中某些实现需要 &self 的生命周期?

How to define an adapter trait where some implementations need a lifetime on &self?

我正在为不同的键值存储编写一组基准测试,并希望有一个适配器特性可以在所有基准测试中使用,然后为每个键值存储实现它。

这对他们中的两个人来说效果很好。但是第三个要求我在trait上加lifetime,和borrow checker折腾了一整天,我好像还是做不对

我已经将它提炼成这个最小的复制品:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=54fec74cb70c63c03f25ec7a9dfc7e60

我不明白的是为什么 txn 上的借用比 benchmark() 的范围长。在我看来,它应该只为那一行而活。

如何定义 AdapterTransaction 特征来解决这个问题,它仍然允许实现选择自己的 return 类型?

编辑

补充说我需要能够使用具有工厂特性的 AdapterTransaction 实现

first playground 的主要问题是 &self 的生命周期与特性的通用生命周期相同。

pub trait AdapterTransaction<'a, T: AsRef<[u8]>> {
    fn get(&'a self, key: &[u8]) -> Option<T>;
}

因为它们是相同的,所以它要求底层类型的借用至少与类型本身一样长。这是不正确的,因为即使类型被拥有,借用也只会在函数调用期间持续。在 benchmark<'a,...>() 中,生命周期 'a 由调用者选择,并且该函数内的借用不可能足够长。删除 benchmark 上的 'a 参数并将其替换为排名更高的特征边界 (playground)。

fn benchmark<U: AsRef<[u8]>, T: for<'a> AdapterTransaction<'a, U>>(txn: T)

在此示例中,调用者不再选择 'a,因此编译器可以自由使用调用的有效生命周期。

至于你问题的第二部分,特征可以定义关联类型,这些类型可以根据实现而改变。您可以拥有一个具有关联 Output 的特征,它可以针对每个已实现的类型进行更改。泛型参数与关联类型有很大的不同,因为在前一种情况下,您可以为同一类型实现特征的多个泛型变体。 (例如,From<T> 就是这样工作的)。


pub trait AdapterTransaction<'a> {
    type Output;

    fn get(&'a self, key: &[u8]) -> Option<Self::Output>;
}

impl<'a> AdapterTransaction<'a> for AdapterImpl {
    type Output = &'a [u8];
    
    fn get(&'a self, key: &[u8]) -> Option<Self::Output> {
        Some(self.txn.get(&key))
    }
}

fn benchmark<T>(txn: T)
 where
    for<'a> T: AdapterTransaction<'a>,
{
    let _ = txn.get(&[]).unwrap();
}

编辑: 我最初的一些假设并不准确,如果以非冲突方式使用特征生命周期,则没有必要在 &'a 类型上实施。