期望一个实现“Fn”特征的闭包,但这个闭包只在 actix web 中实现“FnOnce”
expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` in actix web
我正在使用 actix web,我正在尝试 return 闭包中的异步函数,但出现以下错误:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server.rs:134:33
|
133 | ... web::get().to(
| -- the requirement to implement `Fn` derives from here
134 | / ... move |router: web::Data<Arc<Router>>,
135 | | ... headers: web::Data<Arc<Headers>>,
136 | | ... stream: web::Payload,
137 | | ... req: HttpRequest| async {
| |_________________________________________________________-
138 | || ... start_web_socket(req, stream, params).await
139 | || ... },
| || ^
| ||___________________________|
| |____________________________this closure implements `FnOnce`, not `Fn`
| closure is `FnOnce` because it moves the variable `params` out of its environment
error: aborting due to previous error; 1 warning emitted
这是导致错误的代码段。我已尝试尽可能多地记录代码。
我试过将变量移入和移出移动块,并尝试将它们放置在不同的地方,但没有成功。
应该怎么做而不是这样做?
pub fn start(
&mut self,
py: Python,
url: String,
port: u16,
socket: &PyCell<SocketHeld>,
name: String,
workers: usize,
) -> PyResult<()> {
if STARTED
.compare_exchange(false, true, SeqCst, Relaxed)
.is_err()
{
println!("Already running...");
return Ok(());
}
println!("{}", name);
let borrow = socket.try_borrow_mut()?;
let held_socket: &SocketHeld = &*borrow;
let raw_socket = held_socket.get_socket();
println!("Got our socket {:?}", raw_socket);
let router = self.router.clone();
let headers = self.headers.clone();
let directories = self.directories.clone();
let workers = Arc::new(workers);
let asyncio = py.import("asyncio").unwrap();
let event_loop = asyncio.call_method0("new_event_loop").unwrap();
asyncio
.call_method1("set_event_loop", (event_loop,))
.unwrap();
let event_loop_hdl = PyObject::from(event_loop);
thread::spawn(move || {
//init_current_thread_once();
actix_web::rt::System::new().block_on(async move {
let addr = format!("{}:{}", url, port);
println!("The number of workers are {}", workers.clone());
HttpServer::new(move || {
let mut app = App::new();
let event_loop_hdl = event_loop_hdl.clone();
let directories = directories.read().unwrap();
let router_copy = router.clone();
// this loop matches three types of directory serving
// 1. Serves a build folder. e.g. the build folder generated from yarn build
// 2. Shows file listing
// 3. Just serves the file without any redirection to sub links
for directory in directories.iter() {
if let Some(index_file) = &directory.index_file {
app = app.service(
Files::new(&directory.route, &directory.directory_path)
.index_file(index_file)
.redirect_to_slash_directory(),
);
} else if directory.show_files_listing {
app = app.service(
Files::new(&directory.route, &directory.directory_path)
.redirect_to_slash_directory()
.show_files_listing(),
);
} else {
app = app
.service(Files::new(&directory.route, &directory.directory_path));
}
}
app = app
.app_data(web::Data::new(router.clone()))
.app_data(web::Data::new(headers.clone()));
let web_socket_map = router_copy.get_web_socket_map().unwrap();
for elem in (web_socket_map).iter() {
let route = elem.key().clone();
let params = elem.value().clone();
app = app.route(
&route,
web::get().to(
move |router: web::Data<Arc<Router>>,
headers: web::Data<Arc<Headers>>,
stream: web::Payload,
req: HttpRequest| async {
start_web_socket(req, stream, params).await
},
),
)
}
app.default_service(web::route().to(move |router, headers, payload, req| {
pyo3_asyncio::tokio::scope_local(event_loop_hdl.clone(), async move {
index(router, headers, payload, req).await
})
}))
})
.keep_alive(KeepAlive::Os)
.workers(*workers.clone())
.client_timeout(0)
.listen(raw_socket.try_into().unwrap())
.unwrap()
.run()
.await
.unwrap();
});
});
----更新----
感谢您的建议。在更新 params
时,我能够摆脱原来的错误,但我得到了一个类似但新的错误:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server.rs:134:33
|
133 | ... web::get().to(
| -- the requirement to implement `Fn` derives from here
134 | / ... |router: web::Data<Arc<Router>>,
135 | | ... headers: web::Data<Arc<Headers>>,
136 | | ... stream: web::Payload,
137 | | ... req: HttpRequest| async move {
| |_________________________________________________________-
138 | || ... start_web_socket(req, stream, params.clone()).await
139 | || ... },
| || ^
| ||___________________________|
| |____________________________this closure implements `FnOnce`, not `Fn`
| closure is `FnOnce` because it moves the variable `params` out of its environment
新代码段如下所示:
let web_socket_map = router_copy.get_web_socket_map().unwrap();
for elem in (web_socket_map).iter() {
let route = elem.key().clone();
let params = elem.value().clone();
app = app.route(
&route,
web::get().to(
|router: web::Data<Arc<Router>>,
headers: web::Data<Arc<Headers>>,
stream: web::Payload,
req: HttpRequest| async move {
start_web_socket(req, stream, params.clone()).await
},
),
)
}
我已经尝试在 async move
中克隆参数,但它仍然给出相同的错误。
web::get() returns 一个 Route, and web::get().to(...) is a Route method that expects a Handler。 Handler 应该是一个异步函数 (async fn
) - 调用时 returns 一个 Future 的函数。
问题是在你的代码中你传递了一个异步块,这是一个未来。
An async block is a variant of a block expression which evaluates to a future.
所以你的代码:
async move |...| { // Future
start_web_socket(req, stream, params.clone()).await
}
可以转换为:
move |...| { // Handler Fn
async move { // Future
start_web_socket(req, stream, params.clone()).await
}
}
这相当于:
move |...| { // Handler Fn
start_web_socket(req, stream, params.clone()) // Future
}
因为当你在没有 .await
的情况下调用 async fn start_web_socket
时,你会得到一个 Future。
调试此类事物的一个技巧是将事物分配给中间变量,并检查编译器为它们推断的类型。
我正在使用 actix web,我正在尝试 return 闭包中的异步函数,但出现以下错误:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server.rs:134:33
|
133 | ... web::get().to(
| -- the requirement to implement `Fn` derives from here
134 | / ... move |router: web::Data<Arc<Router>>,
135 | | ... headers: web::Data<Arc<Headers>>,
136 | | ... stream: web::Payload,
137 | | ... req: HttpRequest| async {
| |_________________________________________________________-
138 | || ... start_web_socket(req, stream, params).await
139 | || ... },
| || ^
| ||___________________________|
| |____________________________this closure implements `FnOnce`, not `Fn`
| closure is `FnOnce` because it moves the variable `params` out of its environment
error: aborting due to previous error; 1 warning emitted
这是导致错误的代码段。我已尝试尽可能多地记录代码。
我试过将变量移入和移出移动块,并尝试将它们放置在不同的地方,但没有成功。
应该怎么做而不是这样做?
pub fn start(
&mut self,
py: Python,
url: String,
port: u16,
socket: &PyCell<SocketHeld>,
name: String,
workers: usize,
) -> PyResult<()> {
if STARTED
.compare_exchange(false, true, SeqCst, Relaxed)
.is_err()
{
println!("Already running...");
return Ok(());
}
println!("{}", name);
let borrow = socket.try_borrow_mut()?;
let held_socket: &SocketHeld = &*borrow;
let raw_socket = held_socket.get_socket();
println!("Got our socket {:?}", raw_socket);
let router = self.router.clone();
let headers = self.headers.clone();
let directories = self.directories.clone();
let workers = Arc::new(workers);
let asyncio = py.import("asyncio").unwrap();
let event_loop = asyncio.call_method0("new_event_loop").unwrap();
asyncio
.call_method1("set_event_loop", (event_loop,))
.unwrap();
let event_loop_hdl = PyObject::from(event_loop);
thread::spawn(move || {
//init_current_thread_once();
actix_web::rt::System::new().block_on(async move {
let addr = format!("{}:{}", url, port);
println!("The number of workers are {}", workers.clone());
HttpServer::new(move || {
let mut app = App::new();
let event_loop_hdl = event_loop_hdl.clone();
let directories = directories.read().unwrap();
let router_copy = router.clone();
// this loop matches three types of directory serving
// 1. Serves a build folder. e.g. the build folder generated from yarn build
// 2. Shows file listing
// 3. Just serves the file without any redirection to sub links
for directory in directories.iter() {
if let Some(index_file) = &directory.index_file {
app = app.service(
Files::new(&directory.route, &directory.directory_path)
.index_file(index_file)
.redirect_to_slash_directory(),
);
} else if directory.show_files_listing {
app = app.service(
Files::new(&directory.route, &directory.directory_path)
.redirect_to_slash_directory()
.show_files_listing(),
);
} else {
app = app
.service(Files::new(&directory.route, &directory.directory_path));
}
}
app = app
.app_data(web::Data::new(router.clone()))
.app_data(web::Data::new(headers.clone()));
let web_socket_map = router_copy.get_web_socket_map().unwrap();
for elem in (web_socket_map).iter() {
let route = elem.key().clone();
let params = elem.value().clone();
app = app.route(
&route,
web::get().to(
move |router: web::Data<Arc<Router>>,
headers: web::Data<Arc<Headers>>,
stream: web::Payload,
req: HttpRequest| async {
start_web_socket(req, stream, params).await
},
),
)
}
app.default_service(web::route().to(move |router, headers, payload, req| {
pyo3_asyncio::tokio::scope_local(event_loop_hdl.clone(), async move {
index(router, headers, payload, req).await
})
}))
})
.keep_alive(KeepAlive::Os)
.workers(*workers.clone())
.client_timeout(0)
.listen(raw_socket.try_into().unwrap())
.unwrap()
.run()
.await
.unwrap();
});
});
----更新----
感谢您的建议。在更新 params
时,我能够摆脱原来的错误,但我得到了一个类似但新的错误:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server.rs:134:33
|
133 | ... web::get().to(
| -- the requirement to implement `Fn` derives from here
134 | / ... |router: web::Data<Arc<Router>>,
135 | | ... headers: web::Data<Arc<Headers>>,
136 | | ... stream: web::Payload,
137 | | ... req: HttpRequest| async move {
| |_________________________________________________________-
138 | || ... start_web_socket(req, stream, params.clone()).await
139 | || ... },
| || ^
| ||___________________________|
| |____________________________this closure implements `FnOnce`, not `Fn`
| closure is `FnOnce` because it moves the variable `params` out of its environment
新代码段如下所示:
let web_socket_map = router_copy.get_web_socket_map().unwrap();
for elem in (web_socket_map).iter() {
let route = elem.key().clone();
let params = elem.value().clone();
app = app.route(
&route,
web::get().to(
|router: web::Data<Arc<Router>>,
headers: web::Data<Arc<Headers>>,
stream: web::Payload,
req: HttpRequest| async move {
start_web_socket(req, stream, params.clone()).await
},
),
)
}
我已经尝试在 async move
中克隆参数,但它仍然给出相同的错误。
web::get() returns 一个 Route, and web::get().to(...) is a Route method that expects a Handler。 Handler 应该是一个异步函数 (async fn
) - 调用时 returns 一个 Future 的函数。
问题是在你的代码中你传递了一个异步块,这是一个未来。
An async block is a variant of a block expression which evaluates to a future.
所以你的代码:
async move |...| { // Future
start_web_socket(req, stream, params.clone()).await
}
可以转换为:
move |...| { // Handler Fn
async move { // Future
start_web_socket(req, stream, params.clone()).await
}
}
这相当于:
move |...| { // Handler Fn
start_web_socket(req, stream, params.clone()) // Future
}
因为当你在没有 .await
的情况下调用 async fn start_web_socket
时,你会得到一个 Future。
调试此类事物的一个技巧是将事物分配给中间变量,并检查编译器为它们推断的类型。