简体   繁体   English

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

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

I'm writing a set of benchmarks for different key-value stores, and would like to have a single adapter trait that I can use in all the benchmarks, and then implement it for each key-value store.我正在为不同的键值存储编写一组基准,并希望有一个可以在所有基准中使用的适配器特征,然后为每个键值存储实现它。

This worked well for two of them.这对他们中的两个人来说效果很好。 However, the third required me to add a lifetime on my trait, and after fighting the borrow checker for an entire day, I still can't seem to get it right.然而,第三个要求我在我的特质上增加一生,在与借阅检查员战斗了一整天后,我似乎仍然无法做对。

I've distilled it down to this minimal repro: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=54fec74cb70c63c03f25ec7a9dfc7e60我已经把它提炼成这个最小的重现: https ://play.rust-lang.org/ ? version = stable & mode = debug & edition = 2018 & gist = 54fec74cb70c63c03f25ec7a9dfc7e60

What I don't understand is why the borrow on txn lives longer than the scope of benchmark() .我不明白的是为什么txn上的借用寿命比benchmark()的范围长。 It seems to me that it should live for only that one line.在我看来,它应该只存在那一行。

How can I define the AdapterTransaction trait to resolve this, that still allows implementations to choose their own return type?我如何定义AdapterTransaction trait 来解决这个问题,仍然允许实现选择自己的返回类型?

edit编辑

added that I need to be able to use the AdapterTransaction implementations with a factory trait补充说我需要能够使用带有工厂特征的AdapterTransaction实现

The main problem in your first playground is the lifetime on &self being the same as the generic lifetime on the trait.一个 Playground的主要问题是&self的生命周期与 trait 的通用生命周期相同。

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

Because they are the same, it requires the borrow of the underlying type to live at least as long as the type itself.因为它们是相同的,所以它需要底层类型的借用至少与类型本身一样长。 This isn't true because even though the type is owned, the borrow would only last for the duration of the function call.这不是真的,因为即使类型是拥有的,借用也只会在函数调用期间持续。 In benchmark<'a,...>() , the lifetime 'a is picked by the caller, and there is no way a borrow within that function can be long enough.benchmark<'a,...>() ,生命周期'a由调用者选择,并且该函数中的借用不可能足够长。 There would have been a quick fix to remove the 'a parameter on benchmark and replace it with a higher ranked trait bound ( playground ).将有一个快速解决方案来删除benchmark上的'a参数并将其替换为更高排名的特征边界( playground )。

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

In this example, 'a isn't chosen by the caller anymore, so the compiler is free to use a valid lifetime for the call.在这个例子中, 'a不再被调用者选择,所以编译器可以自由地为调用使用一个有效的生命周期。

As for the 2nd part of your question, traits can define associated types which can change depending on the implementation.至于问题的第二部分,特征可以定义关联类型,这些类型可以根据实现而改变。 You could have a trait that has an associated Output , which can change for each implemented type.您可以拥有一个具有关联Output ,该特征可以针对每个实现的类型进行更改。 There is a big difference with doing a generic param vs an associated type since in the former case, you are allowed to implement multiple generic variants of a trait for the same type.执行泛型参数与关联类型有很大不同,因为在前一种情况下,您可以为同一类型实现一个特征的多个泛型变体。 (This is how From<T> works, for example). (例如,这就是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();
}

Edit: Some of my initial assumptions weren't exact, it's not necessary to implement on &'a Type if the trait lifetime is used in a non-conflicting way.编辑:我最初的一些假设并不准确,如果以不冲突的方式使用 trait 生命周期,则没有必要在 &'a Type 上实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM