使用异步 (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
});
请注意,由于您现在正在使用 Rc
和 RefCell
,您可能需要继续将 ClientContext
结构转换为存储 Rc > 而不是 &mut State
。可以继续使用 &mut State
来做某些事情,但是你的 &mut State
将与 RefMut
的生命周期相关联,如果你让它一直存活到下一次闭包运行时,借用将恐慌(或者如果你使用 try_
变体会失败)。
另请记住,如果您决定要在反应器中使用多个线程,则只需将 Rc
更改为 Arc
, and RefCell
to Mutex
,这是在需要时非常自然的转换。
我正在使用 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
});
请注意,由于您现在正在使用 Rc
和 RefCell
,您可能需要继续将 ClientContext
结构转换为存储 Rc > 而不是 &mut State
。可以继续使用 &mut State
来做某些事情,但是你的 &mut State
将与 RefMut
的生命周期相关联,如果你让它一直存活到下一次闭包运行时,借用将恐慌(或者如果你使用 try_
变体会失败)。
另请记住,如果您决定要在反应器中使用多个线程,则只需将 Rc
更改为 Arc
, and RefCell
to Mutex
,这是在需要时非常自然的转换。