如何在哈希映射字段中保存对处理程序的引用

How to save reference to handler in hashmap field

我正在学习 Rust 并尝试编写一个 websocket 服务器。逻辑如下:WSConnectionFactory 创建 WSHandler 来处理传入的消息并根据任意规则将它们发送到其他客户端。 问题是我不知道如何实现这种行为。

限制:我无法更改工厂和处理程序特征的签名,因为它们由 ws-rs 库提供。

问题:如何使用RefCell/Cell实现这个?

extern crate rand;
extern crate rustc_serialize;
extern crate ws;
#[macro_use]
extern crate log;
#[macro_use]
extern crate env_logger;

use std::cell::RefCell;
use std::collections::HashMap;
use rand::random;
use ws::{Factory, Sender, Handler, Handshake, Message, CloseCode, WebSocket};
use ws::Result as WSResult;
use ws::util::Token;

struct WSConnectionFactory<'p> {
    handlers: HashMap<&'p u32, RefCell<&'p WSHandler<'p>>>,
}

#[derive(Debug)]
struct WSHandler<'h> {
    uid: &'h u32,
    ws: RefCell<&'h Sender>,
}

impl<'p> Factory for WSConnectionFactory<'p> {
    type Handler = WSHandler<'p>;

    fn connection_made(&mut self, ws: Sender) -> Self::Handler {
        println!("factory.connection_made token={:?}", &ws.token());
        let uid = &random::<u32>();
        let handler = WSHandler {
            uid: uid,
            ws: RefCell::new(&ws),
        };
        self.handlers.insert(uid, RefCell::new(&handler));
        handler
    }
}

impl<'h> Handler for WSHandler<'h> {
    fn on_open(&mut self, _handshake: Handshake) -> WSResult<()> {
        println!("handler.on_open");
        Ok(())
    }
    fn on_message(&mut self, msg: Message) -> WSResult<()> {
        println!("handler.on_message {:?}", msg);
        Ok(())
    }
    fn on_timeout(&mut self, _token: Token) -> WSResult<()> {
        println!("handler.on_timeout {:?}", _token);
        Ok(())
    }
    fn on_close(&mut self, code: CloseCode, reason: &str) {
        println!("handler.on_close code={:?}, reason={:?}", code, reason);
    }
}

fn main() {
    let factory = WSConnectionFactory { handlers: HashMap::new() };
    let ws_socket = WebSocket::new(factory).expect("Can't create WebSocket");
    ws_socket.listen("127.0.0.1:8080").expect("Can't bind to socket");
}

您正在尝试从 connection_made return 一个 WSHandler,同时还在 WSConnectionFactory 结构中存储对 WSHandler 的引用。这是不可能的(借用指针),因为通过 returning a WSHandler,你无法控制它会发生什么(它可能被移动或丢弃,这会使你的指针无效)。当您应该直接存储值时,您也在存储借用的指针。

WSConnectionFactory creates WSHandler which handle incoming messages and send them to other clients according to arbitrary rules.

如果你想向其他客户端发送消息,你实际上需要一个Sender,而不是WSHandler。值得庆幸的是,Sender 实现了 Clone,通过快速查看代码,克隆一个 Sender 应该会给你第二个 "handle" 到相同的端点。因此,您应该在 HashMap 中放置 Sender,而不是 WSHandler.

extern crate rand;
extern crate rustc_serialize;
extern crate ws;
#[macro_use]
extern crate log;
#[macro_use]
extern crate env_logger;

use std::collections::HashMap;
use rand::random;
use ws::{Factory, Sender, Handler, Handshake, Message, CloseCode, WebSocket};
use ws::Result as WSResult;
use ws::util::Token;

struct WSConnectionFactory {
    handlers: HashMap<u32, Sender>,
}

#[derive(Debug)]
struct WSHandler {
    uid: u32,
    ws: Sender,
}

impl Factory for WSConnectionFactory {
    type Handler = WSHandler;

    fn connection_made(&mut self, ws: Sender) -> Self::Handler {
        println!("factory.connection_made token={:?}", &ws.token());
        let uid = random::<u32>();
        let handler = WSHandler {
            uid: uid,
            ws: ws.clone(),
        };
        self.handlers.insert(uid, ws);
        handler
    }
}

impl Handler for WSHandler {
    fn on_open(&mut self, _handshake: Handshake) -> WSResult<()> {
        println!("handler.on_open");
        Ok(())
    }
    fn on_message(&mut self, msg: Message) -> WSResult<()> {
        println!("handler.on_message {:?}", msg);
        Ok(())
    }
    fn on_timeout(&mut self, _token: Token) -> WSResult<()> {
        println!("handler.on_timeout {:?}", _token);
        Ok(())
    }
    fn on_close(&mut self, code: CloseCode, reason: &str) {
        println!("handler.on_close code={:?}, reason={:?}", code, reason);
    }
}

fn main() {
    let factory = WSConnectionFactory { handlers: HashMap::new() };
    let ws_socket = WebSocket::new(factory).expect("Can't create WebSocket");
    ws_socket.listen("127.0.0.1:8080").expect("Can't bind to socket");
}