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...
Also I would be happy for another code-improvements
try this
let admin_filter = move |msg: Message| {
// clone the variable from the environment to capture it
let admin_repo = admin_repo.clone();
async move {
match msg.from {
None => false,
Some(user) => admin_repo.is_user_admin(user.id.0).await,
}
}
};
let handler = teloxide::types::Update::filter_message().branch(
dptree::filter_async(admin_filter)
// .filter_command::<AdminCommand>()
.endpoint(answer_admin_command),
);
Ref:
Why don’t async move closures implement Fn?
Remark:
your question is very complex, try see Minimal, Reproducible Example, to learn to write cleaner question.