如何在Actix actor中反序列化消息?

时间:2018-10-13 12:56:40

标签: rust lifetime rust-actix

我的意图是通过WebSocket接收事件,并在main的闭包上使用它们。 当消息为纯文本(String)时,此方法有效,但想法是将该文本反序列化为某些结构。

在此示例中,我仅添加了DataErrorEvent,但是在其他情况下,它可能有所不同,因此我使用了泛型来做到这一点,但是我有点迷路了。编译器已经提出了我尝试过的一些建议,但是我不知道如何“强制”将消息强制转换为特定类型(在此示例中为Data,但是EventManager可能是在其他部分上使用,因此应该是通用的。

我已经附上了这段代码,试图显示我的想法,尽管它没有编译:

events.rs

use actix::*;
use actix_web::ws::{Client, Message, ProtocolError};
use futures::Future;

use serde::de;
use serde_json::from_str;

struct MyActor<T> {
    manager: EventManager<T>,
}

impl<T: 'static> Actor for MyActor<T> {
    type Context = Context<Self>;
}

impl<T: 'static> StreamHandler<Message, ProtocolError> for MyActor<T> {
    fn handle(&mut self, msg: Message, _ctx: &mut Context<Self>) {
        match msg {
            Message::Text(text) => {
                debug!("Received {}", text);

                for idx in 0..self.manager.events.len() {
                    let data =
                        from_str(&text).expect(&format!("Error when deserializing {:?}", text));
                    (self.manager.events[idx].handler)(data)
                }
            }
            _ => panic!(),
        }
    }
}

pub struct Event<T> {
    handler: Box<Fn(T) + 'static>,
}

pub struct EventManager<T> {
    events: Vec<Event<T>>,
}

impl<T: 'static> EventManager<T>
where
    T: serde::Deserialize<'static>,
{
    pub fn new() -> Self {
        Self { events: vec![] }
    }

    pub fn capture<F>(&mut self, function: F)
    where
        F: for<'h> Fn(T) + 'static,
    {
        let event = Event {
            handler: Box::new(function),
        };
        self.events.push(event);
    }

    pub fn run(self) {
        let runner = System::new("example");

        debug!("run");

        Arbiter::spawn(
            Client::new("example")
                .connect()
                .map(|(reader, _writer)| {
                    MyActor::create(|ctx| {
                        MyActor::add_stream(reader, ctx);
                        MyActor { manager: self }
                    });
                })
                .map_err(|err| {}),
        );

        runner.run();
    }
}

main.rs

#[macro_use]
extern crate log;
extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate futures;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

pub mod events;

use actix::*;
use serde::de;
use serde::de::{Deserialize, Deserializer};

use events::EventManager;

#[derive(Debug, Message, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Data {
    Error(Error),
    Event(Event),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Error {
    message: String,
    code: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Event {
    name: String,
    content: String,
}

fn main() {
    env_logger::init();

    let mut client = EventManager::<Data>new();

    client.capture(|data| debug!("event: {:?}", data));
    client.run();
}

所有代码都可以在https://github.com/foochi/how-deserialize-within-actix

中看到

1 个答案:

答案 0 :(得分:1)

有一些修复程序可以使其编译。

进行编译的诀窍是使用Higher-Rank Trait Bounds (HTRB)特质范围而不是声明'static的生存期。

遵循编译器建议并绑定T: serde::Deserialize<'_>特性:

impl<T> StreamHandler<Message, ProtocolError> for MyActor<T>
where
    for<'de> T: serde::Deserialize<'de> + 'static,

然后更改与具有HTRB特性的Deserialize<'static>关联的EventManager特性绑定,使其与StreamHandler实现的要求兼容:

impl<T: 'static> EventManager<T>
where
    for<'de> T: serde::Deserialize<'de>,

最后,如果您更正了该行,请使用正确的sintax创建客户端:

let mut client: EventManager<Data> = EventManager::new();

示例代码应编译。

注意:对于capture,使用较高特质约束来声明Fn的要求是多余的,只需执行以下操作:

pub fn capture<F>(&mut self, function: F)
where
    F: Fn(T) + 'static,