I'm writing a bot on Rust via teloxide + tokio. I have this run method (called from main)
pub async fn run() {
dotenv::dotenv().ok();
pretty_env_logger::init();
log::info!("Starting command bot...");
let bot = Bot::from_env();
let pool = PgPool::connect(&dotenv::var("DATABASE_URL").unwrap())
.await
.unwrap();
let admin_repo = AdminRepository::new(pool.clone());
let worker_repo = WorkerRepository::new(pool.clone());
let client_repo = ClientRepository::new(pool.clone());
let handler = Update::filter_message()
.branch(
dptree::entry()
.filter_command::<BaseCommand>()
.endpoint(answer_base_command),
)
.branch(
dptree::filter_async(|msg: Message| async move {
match msg.from {
None => false,
Some(user) => admin_repo.is_user_admin(user.id.0).await,
}
})
.filter_command::<AdminCommand>()
.endpoint(answer_admin_command),
)
.branch(
dptree::filter_async(|msg: Message| async move {
match msg.from {
None => false,
Some(user) => worker_repo.is_user_worker(user.id.0).await,
}
})
.filter_command::<WorkerCommand>()
.endpoint(answer_worker_command),
)
.branch(
dptree::filter_async(|msg: Message| async move {
match msg.from {
None => false,
Some(user) => client_repo.is_user_client(user.id.0).await,
}
})
.filter_command::<ClientCommand>()
.endpoint(answer_client_command),
);
let bot_state = BotState {
user_repo: UserRepository::new(pool.clone()),
worker_repo: WorkerRepository::new(pool.clone()),
admin_repo: AdminRepository::new(pool.clone()),
client_repo: ClientRepository::new(pool.clone()),
};
Dispatcher::builder(bot, handler)
.dependencies(dptree::deps![bot_state])
.default_handler(|upd| async move {
log::warn!("Unhandled update: {:?}", upd);
})
.error_handler(LoggingErrorHandler::with_custom_text("An error"))
.enable_ctrlc_handler()
.build()
.dispatch()
.await;
}
I have this problev while cargo check for each branch with async_filter
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/lib.rs:47:34
|
47 | dptree::filter_async(|msg: Message| async move {
| -------------------- -^^^^^^^^^^^^^
| | |
| _____________|____________________this closure implements `FnOnce`, not `Fn`
| | |
| | required by a bound introduced by this call
48 | | match msg.from {
49 | | None => false,
50 | | Some(user) => admin_repo.is_user_admin(user.id.0).await,
| | ---------- closure is `FnOnce` because it moves the variable `admin_repo` out of its environment
51 | | }
52 | | })
| |_____________- the requirement to implement `Fn` derives from here
|
= note: required for `{closure@src/lib.rs:47:34: 47:48}` to implement `Injectable<DependencyMap, bool, (teloxide::prelude::Message,)>`
note: required by a bound in `teloxide::dptree::filter_async`
--> /home/skyman/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dptree-0.3.0/src/handler/filter.rs:35:11
|
31 | pub fn filter_async<'a, Pred, Input, Output, FnArgs, Descr>(
| ------------ required by a bound in this function
...
35 | Pred: Injectable<Input, bool, FnArgs> + Send + Sync + 'a,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `filter_async`
I tried to remove "move" (just one branch for example)
.branch(
dptree::filter_async(|msg: Message| async {
match msg.from {
None => false,
Some(user) => admin_repo.is_user_admin(user.id.0).await,
}
})
.filter_command::<AdminCommand>()
.endpoint(answer_admin_command),
but getting error with lifetimes
| dptree::filter_async(|msg: Message| async {
| ^^^^^^^^^^^^^^ may outlive borrowed value `admin_repo`
...
50 | Some(user) => admin_repo.is_user_admin(user.id.0).await,
| ---------- `admin_repo` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/lib.rs:47:13
|
47 | / dptree::filter_async(|msg: Message| async {
48 | | match msg.from {
49 | | None => false,
50 | | Some(user) => admin_repo.is_user_admin(user.id.0).await,
51 | | }
52 | | })
| |______________^
help: to force the closure to take ownership of `admin_repo` (and any other referenced variables), use the `move` keyword
|
47 | dptree::filter_async(move |msg: Message| async {
| ++++
if i do changes from cargo, i get another error with lifetime
error: lifetime may not live long enough
--> src/lib.rs:67:54
|
67 | dptree::filter_async(move |msg: Message| async {
| __________________________________-------------------_^
| | | |
| | | return type of closure `{async block@src/lib.rs:67:54: 67:59}` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
68 | | match msg.from {
69 | | None => false,
70 | | Some(user) => client_repo.is_user_client(user.id.0).await,
71 | | }
72 | | })
| |_____________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
How should I change async closure to make it work? I have a feeling that it should be easier then I trying to do...
moveis not the problem, the problem is that the closure can only be called once because it consumes something that was moved into it. What is the signature ofis_user_admin(),is_user_client, etc.? If they takeself, modify them to take&selfinstead.async move {...}block. The difference matters because with async move, every invocation of the closure moves the value into the future, effectively consuming it (from the closure's point of view) and making it inaccessible for further invocation. That's why the closure implements only the most restrictiveFnOncetrait.