英文:
cannot move out of a captured variable in an async `Fn` closure
问题
这是我的代码。在这个程序中,我想要创建一个简单的WebSocket服务器。当用户向ws://{url}/
发送请求时,浏览器将与服务器建立WebSocket连接。
use std::{collections::HashMap, sync::Arc};
use async_std::{prelude::*, sync::Mutex};
use tide_websockets::WebSocket;
use uuid::Uuid;
#[async_std::main]
async fn main() {
let connections = Arc::new(Mutex::new(HashMap::new()));
let mut app = tide::new();
app.at("/").get(WebSocket::new(move |_, mut stream| async move {
let uuid = Uuid::new_v4();
// 在打开新连接时将连接添加到客户端
connections.lock().await.insert(uuid, stream.clone());
// 等待连接关闭
while let Some(Ok(_)) = stream.next().await {}
// 当连接关闭时,从客户端中删除连接
connections.lock().await.remove(&uuid);
Ok(())
}));
// app.bind(url).await
}
当我尝试编译这个程序时,rustc 报错如下:
error[E0507]: 无法从`connections`中移出一个在`Fn`闭包中被捕获的变量
--> src/main.rs:11:57
|
9 | let connections = Arc::new(Mutex::new(HashMap::new()));
| ----------- 被外部捕获的变量
10 | let mut app = tide::new();
11 | app.at("/").get(WebSocket::new(move |_, mut stream| async move {
| ____________________________________--------------------^
| | |
| | 被此`Fn`闭包捕获
12 | | let uuid = Uuid::new_v4();
13 | |
14 | | // 在打开新连接时将连接添加到客户端
15 | | connections.lock().await.insert(uuid, stream.clone());
| | -----------
| | |
| | 变量由于在生成器中的使用而被移出
| | 移出发生在`connections`有类型`Arc<async_std::sync::Mutex<HashMap<Uuid, WebSocketConnection>>>`,它不实现`Copy`特性
... |
23 | | Ok(())
24 | | }));
| |_____^ 移出`connections`发生在此处
有关此错误的更多信息,请尝试运行 `rustc --explain E0507`。
error: 由于先前的错误,无法编译`mre`
以下是Websocket::new
方法的定义(不确定是否有用):
impl<S, H, Fut> WebSocket<S, H>
where
S: Send + Sync + Clone + 'static,
H: Fn(Request<S>, WebSocketConnection) -> Fut + Sync + Send + 'static,
Fut: Future<Output = Result<()>> + Send + 'static,
{
/// 用一个处理函数构建一个新的WebSocket
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
ghostly_apparition: PhantomData,
protocols: Default::default(),
}
}
// ...
}
在发布这个问题之前,我尝试搜索了这个问题。大多数答案要么与问题无关,要么需要修改Websocket::new
方法的源代码。但这个方法不是我自己写的,而是来自第三方库。是否还有解决这个问题的方法?
英文:
Here is my code. In this program, I want to create a simple websocket server. When user sends a request to the ws://{url}/
, the browser will establish a websocket connection with the server.
use std::{collections::HashMap, sync::Arc};
use async_std::{prelude::*, sync::Mutex};
use tide_websockets::WebSocket;
use uuid::Uuid;
#[async_std::main]
async fn main() {
let connections = Arc::new(Mutex::new(HashMap::new()));
let mut app = tide::new();
app.at("/").get(WebSocket::new(move |_, mut stream| async move {
let uuid = Uuid::new_v4();
// Add the connection to clients when opening a new connection
connections.lock().await.insert(uuid, stream.clone());
// Waiting for the connection to be closed
while let Some(Ok(_)) = stream.next().await {}
// Remove the connection from clients when it is closed
connections.lock().await.remove(&uuid);
Ok(())
}));
// app.bind(url).await
}
When I tried to compile this program, the rustc said:
error[E0507]: cannot move out of `connections`, a captured variable in an `Fn` closure
--> src/main.rs:11:57
|
9 | let connections = Arc::new(Mutex::new(HashMap::new()));
| ----------- captured outer variable
10 | let mut app = tide::new();
11 | app.at("/").get(WebSocket::new(move |_, mut stream| async move {
| ____________________________________--------------------_^
| | |
| | captured by this `Fn` closure
12 | | let uuid = Uuid::new_v4();
13 | |
14 | | // Add the connection to clients when opening a new connection
15 | | connections.lock().await.insert(uuid, stream.clone());
| | -----------
| | |
| | variable moved due to use in generator
| | move occurs because `connections` has type `Arc<async_std::sync::Mutex<HashMap<Uuid, WebSocketConnection>>>`, which does not implement the `Copy` trait
... |
23 | | Ok(())
24 | | }));
| |_____^ move out of `connections` occurs here
For more information about this error, try `rustc --explain E0507`.
error: could not compile `mre` due to previous error
And this is the definition of the Websocket::new
method (no sure if it's useful):
impl<S, H, Fut> WebSocket<S, H>
where
S: Send + Sync + Clone + 'static,
H: Fn(Request<S>, WebSocketConnection) -> Fut + Sync + Send + 'static,
Fut: Future<Output = Result<()>> + Send + 'static,
{
/// Build a new WebSocket with a handler function that
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
ghostly_apparition: PhantomData,
protocols: Default::default(),
}
}
// ...
}
I tried searching this problem before posting this question. Most of the answers are either irrelevant, or need to modify the source code of the method (Websocket::new
method here). But this method is not written by me but is from a third-party crate. Is there still any way to resolve this problem?
答案1
得分: 1
WebSocket::new()
的参数必须是一个Fn
闭包,意味着它必须可以被重复调用。
但是,在你的代码中,它在一个async move
内部使用了connections
变量,这意味着它将该变量移动到了async
块内部。出于明显的原因,这只能做一次。
不过,修复很容易。不要移动整个connections
变量,而是需要创建一个新的Arc
引用来引用connections
变量,并将其移动到async move
中。这样,每次调用都会获得它自己的副本,使其与Fn
兼容。
以下是一个编译通过的版本:
use std::{collections::HashMap, sync::Arc};
use async_std::{prelude::*, sync::Mutex};
use tide_websockets::WebSocket;
use uuid::Uuid;
#[async_std::main]
async fn main() {
let connections = Arc::new(Mutex::new(HashMap::new()));
let mut app = tide::new();
app.at("/").get(WebSocket::new(move |_, mut stream| {
let connections = Arc::clone(&connections);
async move {
let uuid = Uuid::new_v4();
// 在打开新连接时将连接添加到客户端
connections.lock().await.insert(uuid, stream.clone());
// 等待连接关闭
while let Some(Ok(_)) = stream.next().await {}
// 在连接关闭时从客户端中删除连接
connections.lock().await.remove(&uuid);
Ok(())
}
}));
// app.bind(url).await
}
希望这可以帮助你解决问题。
英文:
The argument of WebSocket::new()
has to be an Fn
closure, meaning, it must be callable repeatedly.
In your code, however, it internally uses the connections
variable inside of an async move
, meaning it moves the variable into the async
block. This can for obvious reasons only be done once.
It's easy to fix, though. Instead of moving the entire connections
variable in, you need to create a new Arc
reference of the connections
variable and move that one into the async move
. So every invocation gets its own copy of it, making it compatible with Fn
.
Here is a compiling version:
use std::{collections::HashMap, sync::Arc};
use async_std::{prelude::*, sync::Mutex};
use tide_websockets::WebSocket;
use uuid::Uuid;
#[async_std::main]
async fn main() {
let connections = Arc::new(Mutex::new(HashMap::new()));
let mut app = tide::new();
app.at("/").get(WebSocket::new(move |_, mut stream| {
let connections = Arc::clone(&connections);
async move {
let uuid = Uuid::new_v4();
// Add the connection to clients when opening a new connection
connections.lock().await.insert(uuid, stream.clone());
// Waiting for the connection to be closed
while let Some(Ok(_)) = stream.next().await {}
// Remove the connection from clients when it is closed
connections.lock().await.remove(&uuid);
Ok(())
}
}));
// app.bind(url).await
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论