使用异步 (tokio) rust-websocket 在客户端之间共享可变状态

Sharing mutable state between clients using async (tokio) rust-websocket

我正在使用 Rust-websocket 及其基于 Tokio 的异步系​​统在 Rust 中编写一个 websocket 服务器。我可以很好地为客户服务,但是,我不知道如何在客户之间共享可变状态。这里有一些(部分)代码演示了这个问题:

let mut core = Core::new().unwrap();
let handle = core.handle();
let server = Server::bind("localhost:62831", &handle).unwrap();

let mut state = State{
    ...
};

let f = server.incoming()
    .map_err(|InvalidConnection {error, ..}| error)
    .for_each(|upgrade, _)| {
        let f = upgrade.accept()
            .and_then(|s, _| {
                let ctx = ClientContext{
                    // some other per-client values
                    state: &mut state,
                }
                ...
                return s.send(Message::binary(data).into())
                    .and_then(move |s| Ok(s, ctx)); // this could be the complete wrong way to insert context into the stream
            }).and_then(|s, ctx| {
                // client handling code here
            });

            handle.spawn(f
                .map_err(...)
                .map(...)
            );
            return Ok(())
    });

core.run(f).unwrap();

此代码错误:

error[E0373]: closure may outlive the current function, but it borrows `**state`, which is owned by the current function
   --> src/main.rs:111:27
    |
111 |                 .and_then(|(s, _)| {
    |                           ^^^^^^^^ may outlive borrowed value `**state`
...
114 |                         state: &mut state,
    |                                     ----- `**state` is borrowed here
    |
help: to force the closure to take ownership of `**state` (and any other referenced variables), use the `move` keyword, as shown:
    |                 .and_then(move |(s, _)| {

在尝试编译器的建议时,我得到了这个:

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
   --> src/main.rs:111:27
    |
111 |                 .and_then(move |(s, _)| {
    |                           ^^^^^^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure

error: `state` does not live long enough
   --> src/main.rs:114:37
    |
114 |                         state: &mut state,
    |                                     ^^^^^ does not live long enough
...
122 |                 })
    |                 - borrowed value only lives until here
    |
    = note: borrowed value must be valid for the static lifetime...

我还尝试将状态包装在 RefCell 中(在状态本身之后立即创建 RefCell),但是,编译器给出了类似的移动错误,因为它试图移动 RefCell 进入创建客户端上下文的闭包。

您已经非常接近 RefCell. What you need now is an Rc 来包装 RefCell,因此您可以克隆 Rc 而不是捕获 RefCell 本身。

let shared_state = Rc::new(RefCell::new(State::new())));
incoming().for_each(move |s, _| {
    let shared_state = shared_state.clone();  // Left uncaptured
    shared_state.borrow_mut().do_mutable_state_stuff(); // Could panic
});

请注意,由于您现在正在使用 RcRefCell,您可能需要继续将 ClientContext 结构转换为存储 Rc > 而不是 &mut State。可以继续使用 &mut State 来做某些事情,但是你的 &mut State 将与 RefMut 的生命周期相关联,如果你让它一直存活到下一次闭包运行时,借用将恐慌(或者如果你使用 try_ 变体会失败)。

另请记住,如果您决定要在反应器中使用多个线程,则只需将 Rc 更改为 Arc, and RefCell to Mutex,这是在需要时非常自然的转换。