[英]Rust: Generic return types in Traits for implementations that return non object-safe Traits
我是 Rust 的新手,我想通過實施一些小項目來學習這門語言並更好地理解。 我的第一次嘗試是解析從 MQTT 代理收到的 JSON 數據。 我很高興在tornado
和serde
的幫助下很容易做到這一點。 然而,很快出現了一種我想(理想情況下)提取到特征中的模式。
let person_stream = sender.subscribe().filter_map(|data| {
if let Ok(value) = data {
return serde_json::from_slice::<Person>(&value).ok();
}
None
});
其中sender
通常是tokio::sync::*::Sender
並且Person
將實現serde::de::DeserializeOwned
Trait。
因此,我們的目標是實現采用tokio::stream::StreamExt<Item = Vec<u8>>
並將其transform
為另一個StreamExt
的東西,其中關聯類型Item
將實現DeserializOwned
。
我花了很長時間才弄清楚(因為我最初想使用帶有泛型的 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
})
}
雖然這行得通,但我最初想要一個像
trait Transform<T>
{
fn transform(self) -> T;
}
或實現Into
實際上是相同的,我可以為StreamExt<Item = Vec<u8>>
實現。 由於impl Trait
return 不適用於 Traits,這是我認為的唯一選擇。 但是,我在實現這一點時遇到了幾個問題。
tokio::stream::filter_map::FilterMap<_,_>
for T
(這是filter_map()
的返回類型)是不可能的,因為模塊filter_map
是private
。Box<dyn StreamExt>
也是不可能的,因為StreamExt
在幾個函數中返回Self
。 不過,我一開始就不想要堆開銷;) 所以我的問題是:考慮到filter_map()
的返回類型是private
的並且StreamExt
不是對象安全的,我可以在這里做些什么來獲得 Trait 實現的語法糖嗎? 使用會很酷
let person_stream = receiver.transform();
或into()
。 顯然我有一個可行的實現,所以這對我來說並不是一個真正的關鍵問題。 但正如我一開始所說,我想首先對 Rust 有更深入和更好的了解。
我注意到有tokio-serde
crate,但乍一看它只處理框架數據,所以我沒有深入研究它。
PS:當類型推斷失敗時,我實施的免費 function transform
也遇到了問題。 例如,當將新的 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);
}
}
在這種情況下,與像這樣的匯相比,它顯然無法推斷T
in transfer
async fn person_sink(mut receiver: impl StreamExt<Item = Person> + Unpin) {
while let Some(person) = receiver.next().await {
println!("Person: {:#?}", person);
}
}
但是我不想注釋所有類型參數,只注釋它無法推斷的一個。 經過反復試驗,我發現我可以使用
transform::<Person, _, _>(stream);
我完全不知道。 不過,我在文檔中找不到這個。 這是一些隱藏的功能,還是我只是未能正確 rtfm? :)
我認為你需要的東西可以在每晚的type_alias_impl_trait
功能下獲得。 基本上,它允許您在 trait 中編寫關聯類型,然后在實現中,使用impl BaseTrait
語法而不是編寫命名類型。
我懶得用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!"
}
}
請注意()::do_something
實際上如何返回&'static str
但從未提及此類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.