Pass variable throw closure - rust

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?

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 moves bot_name into the closure because since Clone::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 its bot_name

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

Related

Rust fs read dir

I am writing a program that read dir and then if there a dir it should read it
I have tried this code:
pub async fn list_folder() {
let program_data_folder = String::from(std::env::var("ProgramData").expect("err") + "/Microsoft/Windows/Start Menu/Programs");
let path = Path::new(&program_data_folder);
let paths = fs::read_dir(path).unwrap();
for folder in paths {
println!("Name: {}", folder.unwrap().path().display());
if folder.unwrap().path().is_dir() {
fs::read_dir(folder.unwrap().file_name()).unwrap();
}
}
}
Error:
use of moved value: `folder`
value used here after moverustcClick for full compiler diagnostic
listeners.rs(15, 37): `folder` moved due to this method call
listeners.rs(15, 30): help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
listeners.rs(14, 9): move occurs because `folder` has type `Result<DirEntry, std::io::Error>`, which does not implement the `Copy` trait
result.rs(1106, 19): `Result::<T, E>::unwrap` takes ownership of the receiver `self`, which moves `folder`
How can I fix it?
The key is the last line:
Result::<T, E>::unwrap takes ownership of the receiver self, which moves folder
After you unwrap a Result it's not usable any more because the Ok value's been moved out. That means you can't call unwrap() repeatedly.
To fix it, call unwrap() once and save the result, like so:
for folder in paths {
let folder = folder.unwrap();
println!("Name: {}", folder.path().display());
if folder.path().is_dir() {
fs::read_dir(folder.file_name()).unwrap();
}
}
Unwrapping everything is a bad habit, though. It means I/O errors crash the program immediately. It's a lot better to let errors propagate to the caller so it can deal with them however it chooses. This entails making your function return a Result and replacing all the unwraps with ?. Rust makes the best option, ?, the shortest, to encourage good error handling.
pub async fn list_folder() -> std::io::Result<()> {
let program_data_folder = String::from(std::env::var("ProgramData").expect("err") + "/Microsoft/Windows/Start Menu/Programs");
let path = Path::new(&program_data_folder);
let paths = fs::read_dir(path)?;
for folder in paths {
let folder = folder?;
println!("Name: {}", folder.path().display());
if folder.path().is_dir() {
fs::read_dir(folder.file_name())?;
}
}
Ok(())
}

Keep value used in closure for later use

fn callback_test() {
let image = HtmlImageElement::new().unwrap();
let callback: Closure<dyn Fn()> = {
Closure::wrap(Box::new(|| {
image.set_src("foo");
}))
};
image.set_onload(Some(&callback.as_ref().unchecked_ref()));
}
Here is a exemple of what I'm trying to achieve. If I don't use the move keyword before the closure declaration I get a lifetime error, and if I use it I can't assign my callback later in the function. What is the correct way to resolve this issue?
You will have to .clone() it.
The Closure only works with functions that are 'static, meaning they can't hold references to local variables. If it were to allow that, then calling that closure after callback_test() completes would try to use a dangling reference since image has already been dropped.
So move-ing it into the closure is the right move. And since you have to use it again after creating the closure, you will need two copies to work with.
Try this out:
fn callback_test() {
let image = HtmlImageElement::new().unwrap();
let callback: Closure<dyn Fn()> = {
let image = image.clone();
Closure::wrap(Box::new(move || {
image.set_src("foo");
}))
};
image.set_onload(Some(&callback.as_ref().unchecked_ref()));
}

How do I clone a variable before moving it into warp's .then() filter?

I have the following code snippet:
async fn server(config: crate::Config) {
println!("Building server");
let key = hmac::Key::new(hmac::HMAC_SHA256, config.docusign.hmac_key.as_bytes());
let webhook = warp::path("webhook")
.and(warp::post())
.and(warp::body::content_length_limit(4194304))
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.then(|headers: HeaderMap, bytes: Bytes| async move {
match verify_msg(&key, &headers, &bytes) {
Ok(_) => {
println!("Message is Valid!");
process_msg(bytes).await.into_response()
}
Err(string) => {
println!("{string}");
warp::reply::with_status(warp::reply(), http::StatusCode::UNAUTHORIZED)
.into_response()
}
}
});
warp::serve(webhook)
.tls()
.cert_path("cert/cert.pem")
.key_path("cert/key.pem")
.run(([0, 0, 0, 0], 443))
.await;
println!("Shutting down Server");
}
This gives me an error:
expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
this closure implements `FnOnce`, not `Fn`rustc(E0525)
server.rs(20, 4): the requirement to implement `Fn` derives from here
server.rs(20, 9): this closure implements `FnOnce`, not `Fn`
server.rs(21, 22): closure is `FnOnce` because it moves the variable `key` out of its environment
This makes sense, I am using the key variable and thus moving it out of the environment. What I can't figure out is how can I get this async closure to work without moving the key? I've tried cloning it like this: match verify_msg(&key.clone(), &headers, &bytes) but it still doesn't work. I guess that makes sense, since the variable is still referenced inside the closure. So, how do I clone the key before it gets moved?
I was able to get it working with .map() and a regular (non-async) closure, but the process_msg() function is async, so I don't think that would work.
Edit:
The answer from #t56k got me on the right track, but didn't quite work. Going in the direction of putting async blocks inside of a closure and following the compiler's recommendations eventually got me this:
async fn server(config: crate::Config) {
println!("Building server");
let key = hmac::Key::new(hmac::HMAC_SHA256, config.docusign.hmac_key.as_bytes());
let webhook = warp::path("webhook")
.and(warp::post())
.and(warp::body::content_length_limit(4194304))
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.then(move |headers: HeaderMap, bytes: Bytes| {
let key = key.clone();
async move {
match verify_msg(&key, &headers, &bytes) {
Ok(_) => {
println!("Message is Valid!");
process_msg(bytes).await.into_response()
}
Err(string) => {
println!("{string}");
warp::reply::with_status(warp::reply(), http::StatusCode::UNAUTHORIZED)
.into_response()
}
}
}
});
warp::serve(webhook)
.tls()
.cert_path("cert/cert.pem")
.key_path("cert/key.pem")
.run(([0, 0, 0, 0], 443))
.await;
println!("Shutting down Server");
}
which works perfectly for some reason even though I'm using the move keyword. I guess i'm only allowed to move key if it isn't inside of an async block? In any case, my problem is solved, but if anyone could explain why this works I would gladly accept it.
This is untested for your use case, but you can .clone() things before the move to allow them access.
.and_then(|headers: HeaderMap, bytes: Bytes| async {
let key = key.clone();
move {
match verify_msg(key, &headers, &bytes) {
// ...
}
}
});
(Friend got directed here when googling a similar issue, thought I'd provide context for future searchers too)
The main issue here is Rust's lack of support for "true" async closures.
When you move a value into a move || {...}, that value is now owned by the closure object. If calling the closure only needs references to that value, then it can be Fn.
Similarly, when you move a value into an async move {...}, that value becomes a part of the Future object, stored as part of the state when the Future is polled.
The trouble arises when you try to do move || async move {...}. What this does is:
Move the value out of the environment and into the closure object
Move the value out of the closure and into the async block
The closure returns the async block
Moves can only happen once, so the closure can only be called once, because the closure is returning a different Future every time it's called. This is what I meant by there aren't yet true async closures; a true async closure, async move || {...} would construct a single Future the way a closure is just a single object.
The workaround you need to do (and ended up doing) is to:
Move the value into the closure
Before creating a new Future, clone the value inside the closure
Move the cloned value into the Future
Return the Future
Hope this helps explain things!

Safely move or dereference Receiver in a Fn?

I'm working on an app that optionally uses a GUI to display video data that's roughly structured like this:
fn main() {
let (window_tx, window_rx) =
MainContext::channel::<MyStruct>(PRIORITY_DEFAULT);
let some_thread = thread::spawn(move || -> () {
// send data to window_tx
});
let application =
gtk::Application::new(Some("com.my.app"), Default::default());
application.connect_activate(move |app: &gtk::Application| {
build_ui(app, window_rx);
});
application.run();
some_thread.join().unwrap();
}
fn build_ui(application: &gtk::Application, window_rx: Receiver<MyStruct>) {
window_rx.attach( ... );
}
The gtk rust library requires a Fn callback passed to application.connect_activate on startup, so I can't use a FnOnce or FnMut closure to move the glib::Receiver in the callback. The compiler throws this error:
error[E0507]: cannot move out of `window_rx`, a captured variable in an `Fn` closure
I've tried to avoid the move by wrapping window_rx in a Rc, ie:
let r = Rc::new(RefCell::new(window_rx));
application.connect_activate(move |app: &gtk::Application| {
build_ui(app, Rc::clone(&r));
});
But upon dereferencing the Rc in my build_ui function, I get this error:
error[E0507]: cannot move out of an `Rc`
The fallback I've used thus far is to just move the channel creation and thread creation into my build_ui function, but because the GUI is not required, I was hoping to avoid using GTK and the callback entirely if GUI is not used. Is there some way I can either safely move window_rx within a closure or otherwise dereference it in the callback without causing an error?
When you need to move a value out from code that, by the type system but not in practice, could be called more than once, the simple tool to reach for is Option. Wrapping the value in an Option allows it to be swapped with an Option::None.
When you need something to be mutable even though you're inside a Fn, you need interior mutability; in this case, Cell will do. Here's a complete compilable program that approximates your situation:
use std::cell::Cell;
// Placeholders to let it compile
use std::sync::mpsc;
fn wants_fn_callback<F>(_f: F) where F: Fn() + 'static {}
struct MyStruct;
fn main() {
let (_, window_rx) = mpsc::channel::<MyStruct>();
let window_rx: Cell<Option<mpsc::Receiver<MyStruct>>> = Cell::new(Some(window_rx));
wants_fn_callback(move || {
let _: mpsc::Receiver<MyStruct> = window_rx.take().expect("oops, called twice");
});
}
Cell::take() removes the Option<Receiver> from the Cell, leaving None in its place. The expect then removes the Option wrapper (and handles the possibility of the function being called twice by panicking in that case).
Applied to your original problem, this would be:
let window_rx: Option<Receiver<MyStruct>> = Cell::new(Some(window_rx));
application.connect_activate(move |app: &gtk::Application| {
build_ui(app, window_rx.take().expect("oops, called twice"));
});
However, be careful: if the library requires a Fn closure, there might be some condition under which the function could be called more than once, in which case you should be prepared to do something appropriate in that circumstance. If there isn't such a condition, then the library's API should be improved to take a FnOnce instead.

How to re-use a value from the outer scope inside a closure in Rust?

I am trying to add a webserver (hyper) in a small Rust program and I'm getting hit with a move issue.
//main.rs
// Suppose this is something meaningful and used in multiple places inside `main`
let test: String = "Foo".to_string();
// ...
// This comes from here: https://docs.rs/hyper/0.14.8/hyper/service/fn.service_fn.html
// Determines what the web server sends back
let make_svc = make_service_fn(|_conn| async {
Ok::<_, Infallible>(service_fn(|req: Request<Body>| async move {
// I need to somehow read the value of `test` here as well
if req.version() == Version::HTTP_11 {
Ok(Response::new(Body::from(test)))
} else {
Err("not HTTP/1.1, abort connection")
}
}))
});
This produces the following issue:
I understand that String can't have the Copy trait (and eventually I will need to use other more complex types). Essentially, I want to borrow or use a clone of the variable, because the variables will also be used outside of hyper's handler (for example, it could log to a file or be used for aggregate statistics over time).
So my question is, how does it work? How do I need to refactor the closure like so that I can somehow access the values of variables defined (and otherwise used) inside main?
If you want to clone test then you need to clone it outside the closure and then move it into the closure. Note that if you want to change test later on and have these changes reflected inside the closure you will need to use Arc<RefCell> so that both inside and outside the closure have mutable access to the same string.
fn main() {
let mut test: String = "Foo".to_string();
// Clone the string so you can use it in a closure.
let test_for_closure = test.clone();
let closure = || {
println!("{}", test_for_closure);
};
// You can still use test outside the closure
println!("{}", test); // prints "Foo"
closure(); // prints "Foo"
// But if test is changed, test_for_closure will not change
test = "Other".to_string();
println!("{}", test); // prints "Other"
closure(); // prints "Foo"
}
Let me know if you need help on using Arc<RefCell> for shared mutable access to the same string.

Resources