提问人:Mads Ahlquist Jensen 提问时间:3/21/2023 最后编辑:Mads Ahlquist Jensen 更新时间:3/21/2023 访问量:231
在 Hashmap 或类似 Rust 中可重用的异步闭包/函数,Rust
Reuseable async-closures/functions in a Hashmap or alike, Rust
问:
我想创建一个结构,它有四个方法:new(), handle_interaction(), add_shortcut(), async start_server()
该结构应包含一个 Web 服务器,该服务器使用用户添加的可用快捷方式(回调闭包)列表启动。async start_server()
当服务器启动时,我希望有一个端点 (),然后它将接收 JSON 数据。当收到 json 数据时,服务器将确定传入的交互,并同时启动交互闭包。/
我尝试了很多不同的解决方案,我必须说,其中很多都包含了我还不熟悉的 rust 类型和概念。
但是,我现在登陆的代码仍然失败,是这样的: (我用注释突出显示了错误,只是为了让您知道失败的原因)
use axum::{
routing::{get, post},
http::StatusCode,
Json, Router,
};
use std::{net::SocketAddr, future::Future, pin::Pin, collections::HashMap};
use tracing::{debug, info, error};
use futures::{FutureExt, future::BoxFuture};
pub struct Interaction {
r#type: String,
}
#[derive(Default)]
pub struct App {
router: Router,
shortcuts: HashMap<String, Box<dyn Fn(Interaction) -> BoxFuture<'static, ()>>>
}
impl App {
pub fn new() -> Self {
Self::default()
}
async fn handle_interaction(
&self,
Json(interaction): Json<Interaction>
) -> StatusCode {
let t = interaction.r#type.clone();
debug!("Got shortcut");
debug!("{t}");
let closure = self.shortcuts.get(&t).unwrap();
tokio::task::spawn(closure(interaction));
StatusCode::OK
}
pub fn add_shortcut<C, Fut>(
mut self,
callback_id: &str,
fun: C,
) -> Self
where
Fut: Future<Output = ()> + Send + 'static,
C: Fn(Interaction) -> Pin<Box<Fut>>,
{
self.shortcuts.insert(callback_id.to_string(), Box::new(move |interaction| Box::pin(fun(interaction))));
self
}
async fn start(self, addr: SocketAddr) {
let interaction_handler = move |interaction| async move {
self.handle_interaction(interaction).await
};
// ERROR: `interaction_handler` doesn't match axums route-types... Don't know why this is the case either.
let router = Router::new()
.route("/", post(interaction_handler));
debug!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(self.router.into_make_service())
.await
.unwrap();
}
}
async fn shortcut1(i: Interaction) {
println!("Hello, World!");
}
async fn shortcut2(i: Interaction) {
println!("Hello, Other World!")
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
// ERROR: `shortcut1` and `shortcut2` doesn't match input type,
// and i don't know how to do this properly.
let app = App::new()
.add_shortcut("shortcut1", shortcut1)
.add_shortcut("shortcut2", shortcut2);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
app.start(addr).await;
}
其中很多是跟踪和错误、chatgpt 和随机文档的结果——我不熟悉 box、pin 等。
我也读过 Mutex 或 Arc 可能有助于解决这个问题,但我也无法让它起作用。
我希望这里有人能帮助我指出正确的方向!
答:
0赞
Chayim Friedman
3/21/2023
#1
您的代码存在三个主要问题:
Interaction
不实现反序列化
,因此不能与 一起使用。Json
- 处理程序不实现
Handler
,因为不可克隆 + not .self
Send
- 您使用的是本地 .
self.router
router
此外,您不需要任务 - 会为您处理,您也不需要返回 - 只需返回即可。spawn()
axum
StatusCode::OK
()
所有修复后的代码:
use axum::{
body::Body,
http::StatusCode,
routing::{get, post},
Json, Router,
};
use futures::future::BoxFuture;
use serde::Deserialize;
use std::sync::Arc;
use std::{collections::HashMap, future::Future, net::SocketAddr};
#[derive(Deserialize)]
pub struct Interaction {
r#type: String,
}
type Shortcuts = HashMap<String, Box<dyn Fn(Interaction) -> BoxFuture<'static, ()> + Send + Sync>>;
#[derive(Default)]
pub struct App {
shortcuts: Shortcuts,
}
impl App {
pub fn new() -> Self {
Self::default()
}
async fn handle_interaction(shortcuts: Arc<Shortcuts>, Json(interaction): Json<Interaction>) {
let closure = shortcuts.get(&interaction.r#type).unwrap();
closure(interaction);
}
pub fn add_shortcut<C, Fut>(mut self, callback_id: &str, fun: C) -> Self
where
Fut: Future<Output = ()> + Send + 'static,
C: Fn(Interaction) -> Fut + Send + Sync + 'static,
{
self.shortcuts.insert(
callback_id.to_string(),
Box::new(move |interaction| Box::pin(fun(interaction))),
);
self
}
async fn start(self, addr: SocketAddr) {
let shortcuts = Arc::new(self.shortcuts);
let interaction_handler = move |interaction| async move {
Self::handle_interaction(shortcuts, interaction).await
};
let router = Router::new().route("/", post(interaction_handler));
axum::Server::bind(&addr)
.serve(router.into_make_service())
.await
.unwrap();
}
}
async fn shortcut1(i: Interaction) {
println!("Hello, World!");
}
async fn shortcut2(i: Interaction) {
println!("Hello, Other World!")
}
#[tokio::main]
async fn main() {
// ERROR: `shortcut1` and `shortcut2` doesn't match input type,
// and i don't know how to do this properly.
let app = App::new()
.add_shortcut("shortcut1", shortcut1)
.add_shortcut("shortcut2", shortcut2);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
// app.start(addr).await;
}
评论
0赞
Mads Ahlquist Jensen
3/24/2023
非常感谢!起初我以为出了点问题,因为函数没有输出任何东西。然后我注意到没有等待关闭 - 它现在按预期工作!
评论