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

问题描述

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

这对其中两个很有效。然而,第三个要求我在我的特质上添加一个生命周期,并且在与借阅检查员战斗了一整天后,我似乎仍然无法正确完成。

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

我不明白的是为什么 txn 上的借用比 benchmark() 的作用域寿命更长。在我看来,它应该只存在那一行。

如何定义 AdapterTransaction trait 来解决这个问题,仍然允许实现选择自己的返回类型?

编辑

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

解决方法

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

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

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

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

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

至于您问题的第二部分,traits 可以定义关联类型,这些类型可以根据实现而改变。您可以拥有一个具有关联 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 Type 上实现。