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

ryt*_*one 5 asynchronous websocket rust rust-tokio

我正在使用 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();
Run Code Online (Sandbox Code Playgroud)

此代码错误如下:

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, _)| {
Run Code Online (Sandbox Code Playgroud)

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

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...
Run Code Online (Sandbox Code Playgroud)

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

Spa*_*apS 4

你与 . 的关系非常接近RefCell。您现在需要的是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
});
Run Code Online (Sandbox Code Playgroud)

请注意,由于您现在使用Rc's 和RefCell's,您可能需要继续将ClientContext结构转换为存储 Rc> 而不是&mut State。可能可以继续使用&mut State' 来完成某些事情,但是您的&mut State' 将与 的生命周期相关联RefMut,如果您将其保持活动状态直到下一个闭包运行,则借用将发生恐慌(或者如果您使用变try_体则失败)。

另请记住,如果您决定要在反应器中使用多个线程,则只需更改RcArc、 和RefCellMutex这是在需要时非常自然的转换。