[英]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
.我很高兴在
tornado
和serde
的帮助下很容易做到这一点。 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.但是,我在实现这一点时遇到了几个问题。
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_map
是private
。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.