'Pass variable throw closure
I have some async method of structure:
impl ImageBot {
pub async fn run(&self) {
let bot_name = self.bot_name.clone();
let token = self.token.clone();
let http_host = self.http_host.clone();
let web_hook_url = self.web_hook_url.clone();
let bot = teloxide::Bot::new(token).auto_send();
teloxide::repls2::repl_with_listener(
bot.clone(),
|message: Message, bot: AutoSend<teloxide::Bot>| async move {
let name = bot_name;
let command_with_args = parse_command(message.text().unwrap(), name);
if let Some((command, args)) = command_with_args {
command_handler(bot, message.clone(), command, args).await
} else {
//
}
respond(())
},
webhook(bot.clone(), http_host, web_hook_url).await,
)
.await;
}
}
I need to get bot_name
for parse_command:
|message: Message, bot: AutoSend<teloxide::Bot>| async move {
let name = bot_name;
let command_with_args = parse_command(message.text().unwrap(), name);
Now I get error:
Expected a closure that implements the Fn trait, but this closure only implements FnOnce
on this:
|message: Message, bot: AutoSend<teloxide::Bot>| async move
How can I do this?
Solution 1:[1]
The issue you're hitting is a common one currently due to the lack of async closures, and hence reliance on async move
blocks, reformatting a bit:
|message: Message, bot: AutoSend<teloxide::Bot>| {
async move {
let name = bot_name;
[...]
this means bot_name
will be moved into the closure, then on the first call it will be moved into the block from the closure.
This means the first call consumes bot_name
, and in order to do so has to consume the closure itself, therefore the function can't be called repeatedly.
The solution then is to create a copy inside the closure, which you can then move into the block:
move |message: Message, bot: AutoSend<teloxide::Bot>| {
let name = bot_name.clone();
async move {
[...]
- the closure is
move
, this movesbot_name
into the closure because sinceClone::clone
takes a reference otherwise rust will only try to capture a reference, which is unlikely to work out - then we create a local
name
copy, which can be moved inside the block, and the closure doesn't care that it gets dropped during the async process because it still has itsbot_name
Solution 2:[2]
The problem is, teloxide::repls2::repl_with_listener
expects to get a closure, that can be called multiple times, however, the closure you're passing is consuming the name
variable when it's called.
This line is the problem:
let command_with_args = parse_command(message.text().unwrap(), name);
You should rewrite it like so:
let command_with_args = parse_command(message.text().unwrap(), &name);
So that name
is only borrowed every time the closure is invoked, instead of beeing consumed
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | gmoshkin |