Я новичок в Rust, и я хотел выучить язык и лучше понять, реализовав несколько небольших проектов. Моей первой попыткой было проанализировать JSON данных, полученных от MQTT Broker. Мне было очень приятно, как легко было выполнить sh с помощью 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
.
Таким образом, цель состояла в том, чтобы реализовать что-то, что берет tokio::stream::StreamExt<Item = Vec<u8>>
и transform
это в другое StreamExt
, где связанный тип Item
будет реализовывать DeserializOwned
.
Это заняло мне достаточно времени, чтобы что-то выяснить (так как я изначально хотел использовать Trait или функцию с обобщениями), и я придумал
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 вроде
trait Transform<T>
{
fn transform(self) -> T;
}
или реализовать Into
, что на самом деле то же самое, что я мог бы реализовать для StreamExt<Item = Vec<u8>>
. Так как impl Trait
возврат не доступен для черт, это был единственный вариант, который у меня был. Однако я сталкиваюсь с парой проблем, связанных с реализацией этого.
- Использование
tokio::stream::filter_map::FilterMap<_,_>
для T
(что является типом возврата filter_map()
) невозможно, так как модуль filter_map
is private
. - Использование
Box<dyn StreamExt>
также невозможно, так как StreamExt
возвращает Self
в паре функций. Хотя я не хотел, чтобы в первую очередь были использованы служебные данные Heap;)
Поэтому мой вопрос: могу ли я здесь что-нибудь сделать, чтобы получить сахар syntacti c реализации Trait, учитывая факт, что тип возвращаемого значения filter_map()
равен private
, а StreamExt
не является объектно-безопасным? Было бы здорово просто использовать
let person_stream = receiver.transform();
или into()
. Очевидно, у меня есть работающая реализация, так что это не очень важный вопрос для меня. Но, как я сказал в начале, я хотел получить более глубокое и лучшее понимание Rust.
Я заметил, что есть ящик tokio-serde
, но на первый взгляд он имеет дело только с данными в кадрах , поэтому я не углубился в это.
PS: Я также столкнулся с проблемой с бесплатной функцией transform
, которую я реализовал, когда вывод типа завершился неудачей. Например, при передаче нового потока функции, подобной
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
в 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? :)