简体   繁体   English

Rust:特性中的通用返回类型,用于返回非对象安全特性的实现

[英]Rust: Generic return types in Traits for implementations that return non object-safe Traits

I'm new to Rust and I wanted to learn the language and get a better understanding by implementing some small projects.我是 Rust 的新手,我想通过实施一些小项目来学习这门语言并更好地理解。 My first attempt was to parse JSON data received from an MQTT Broker.我的第一次尝试是解析从 MQTT 代理收到的 JSON 数据。 I was very pleased how easy it was to accomplish this with the help of tornado and serde .我很高兴在tornadoserde的帮助下很容易做到这一点。 However, there quickly arose a pattern that I wanted to (ideally) extract into a Trait.然而,很快出现了一种我想(理想情况下)提取到特征中的模式。

let person_stream = sender.subscribe().filter_map(|data| {
    if let Ok(value) = data {
        return serde_json::from_slice::<Person>(&value).ok();
    }
    None
});

where sender typically is a tokio::sync::*::Sender and Person would implement the serde::de::DeserializeOwned Trait.其中sender通常是tokio::sync::*::Sender并且Person将实现serde::de::DeserializeOwned Trait。

So the goal was to implement something that takes a tokio::stream::StreamExt<Item = Vec<u8>> and transform it into another StreamExt where the associated type Item would implement DeserializOwned .因此,我们的目标是实现采用tokio::stream::StreamExt<Item = Vec<u8>>并将其transform为另一个StreamExt的东西,其中关联类型Item将实现DeserializOwned

It took me quite some time to figure something out (since I originally wanted to use a Trait or a function with generics) and I came up with我花了很长时间才弄清楚(因为我最初想使用带有泛型的 Trait 或 function),我想出了

fn transform<T, U, V>(stream: U) -> impl StreamExt<Item = T>
where
    T: serde::de::DeserializeOwned,
    U: StreamExt<Item = Result<Vec<u8>, V>>,
{
    stream.filter_map(|data| {
        if let Ok(value) = data {
            return serde_json::from_slice::<T>(&value).ok();
        }
        None
    })
}

While this works I originally wanted a Trait like虽然这行得通,但我最初想要一个像

trait Transform<T>
{
    fn transform(self) -> T;
}

or implement Into which is actually the same, which I could implement for StreamExt<Item = Vec<u8>> .或实现Into实际上是相同的,我可以为StreamExt<Item = Vec<u8>>实现。 Since impl Trait return is not available for Traits this was the only option I thought I had.由于impl Trait return 不适用于 Traits,这是我认为的唯一选择。 However, I'm facing a couple of issues implementing this.但是,我在实现这一点时遇到了几个问题。

  1. Using tokio::stream::filter_map::FilterMap<_,_> for T (which is the return type of filter_map() ) is not possible since the module filter_map is private .使用tokio::stream::filter_map::FilterMap<_,_> for T (这是filter_map()的返回类型)是不可能的,因为模块filter_mapprivate
  2. Using Box<dyn StreamExt> isn't possible either, since StreamExt returns Self in a couple of functions.使用Box<dyn StreamExt>也是不可能的,因为StreamExt在几个函数中返回Self I didn't want the Heap overhead in the first place though;)不过,我一开始就不想要堆开销;)

So my question is: Is there anything I could do here to get the syntactic sugar of a Trait implementation given the fact that the return type of filter_map() is private and StreamExt isn't object-safe?所以我的问题是:考虑到filter_map()的返回类型是private的并且StreamExt不是对象安全的,我可以在这里做些什么来获得 Trait 实现的语法糖吗? It would be cool to just use使用会很酷

let person_stream = receiver.transform();

or into() .into() Obviously I have a working implementation so this isn't really a critical question for me.显然我有一个可行的实现,所以这对我来说并不是一个真正的关键问题。 But as I said at the beginning I wanted to get a deeper and better understanding about Rust in the first place.但正如我一开始所说,我想首先对 Rust 有更深入和更好的了解。

I noticed that there is the tokio-serde crate, but at first glance it only deals with framed data, so I haven't dug deeper into it.我注意到有tokio-serde crate,但乍一看它只处理框架数据,所以我没有深入研究它。

PS: I also faced a problem with the free function transform I implemented when the type inference fails. PS:当类型推断失败时,我实施的免费 function transform也遇到了问题。 For example when handing of the new stream to a function like例如,当将新的 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 交给 function 时

async fn debug_sink<T>(mut receiver: T)
where
    T: StreamExt + Unpin,
    T::Item: std::fmt::Debug,
{
    while let Some(item) = receiver.next().await {
        println!("Item: {:#?}", item);
    }
}

In this case it obviously couldn't infer T in transfer in contrast to sinks like在这种情况下,与像这样的汇相比,它显然无法推断T in transfer

async fn person_sink(mut receiver: impl StreamExt<Item = Person> + Unpin) {
    while let Some(person) = receiver.next().await {
        println!("Person: {:#?}", person);
    }
}

However I didn't want to annotate all type parameters, only the one it couldn't infer.但是我不想注释所有类型参数,只注释它无法推断的一个。 With some trial and error I found out that I could use经过反复试验,我发现我可以使用

transform::<Person, _, _>(stream);

which I was totally unaware of.我完全不知道。 I couldn't find this in the documentation, though.不过,我在文档中找不到这个。 Is this some hidden feature, or am I just failing to properly rtfm?这是一些隐藏的功能,还是我只是未能正确 rtfm? :) :)

I think that what you need is available in Nightly under the type_alias_impl_trait feature.我认为你需要的东西可以在每晚的type_alias_impl_trait功能下获得。 Basically it allows you to write an associated type in a trait, and then, in the implementation, instead of writing a named type, usie the impl BaseTrait syntax.基本上,它允许您在 trait 中编写关联类型,然后在实现中,使用impl BaseTrait语法而不是编写命名类型。

I'm too lazy to write your code with a type_alias_impl_trait (and you didn't provide a compilable snippet) but here it is a working example ( playground ):我懒得用type_alias_impl_trait编写您的代码(并且您没有提供可编译的代码段),但这是一个工作示例( playground ):

#![feature(type_alias_impl_trait)]

use std::fmt::Debug;

trait Foo {
    type Output: Debug;

    fn do_something() -> Self::Output;
}

impl Foo for () {
    type Output = impl Debug;

    fn do_something() -> Self::Output {
        "hello!"
    }
}

Note how ()::do_something actually returns a &'static str but this type is never mentioned.请注意()::do_something实际上如何返回&'static str但从未提及此类型。

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

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