Keep value used in closure for later use - rust

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()));
}

Related

How do I define the lifetime for a tokio task spawned from a class?

I'm attempting to write a generic set_interval function helper:
pub fn set_interval<F, Fut>(mut f: F, dur: Duration)
where
F: Send + 'static + FnMut() -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
let mut interval = tokio::time::interval(dur);
tokio::spawn(async move {
// first tick is at 0ms
interval.tick().await;
loop {
interval.tick().await;
tokio::spawn(f());
}
});
}
This works fine until it's called from inside a class:
fn main() {}
struct Foo {}
impl Foo {
fn bar(&self) {
set_interval(|| self.task(), Duration::from_millis(1000));
}
async fn task(&self) {
}
}
self is not 'static, and we can't restrict lifetime parameter to something that is less than 'static because of tokio::task.
Is it possible to modify set_interval implementation so it works in cases like this?
Link to playground
P.S. Tried to
let instance = self.clone();
set_interval(move || instance.task(), Duration::from_millis(1000));
but I also get an error: error: captured variable cannot escape FnMut closure body
Is it possible to modify set_interval implementation so it works in cases like this?
Not really. Though spawn-ing f() really doesn't help either, as it precludes a simple "callback owns the object" solution (as you need either both callback and future to own the object, or just future).
I think that leaves two solutions:
Convert everything to shared mutability Arc, the callback owns one Arc, then on each tick it clones that and moves the clone into the future (the task method).
Have the future (task) acquire the object from some external source instead of being called on one, this way the intermediate callback doesn't need to do anything. Or the callback can do the acquiring and move that into the future, same diff.
Incidentally at this point it could make sense to just create the future directly, but allow cloning it. So instead of taking a callback set_interval would take a clonable future, and it would spawn() clones of its stored future instead of creating them anew.
As mentioned by #Masklinn, you can clone the Arc to allow for this. Note that cloning the Arc will not clone the underlying data, just the pointer, so it is generally OK to do so, and should not have a major impact on performance.
Here is an example. The following code will produce the error async block may outlive the current function, but it borrows data, which is owned by the current function:
fn main() {
// 🛑 Error: async block may outlive the current function, but it borrows data, which is owned by the current function
let data = Arc::new("Hello, World".to_string());
tokio::task::spawn(async {
println!("1: {}", data.len());
});
tokio::task::spawn(async {
println!("2: {}", data.len());
});
}
Rust unhelpfully suggests adding move to both async blocks, but that will result in a borrowing error because there would be multiple ownership.
To fix the problem, we can clone the Arc for each task and then add the move keyword to the async blocks:
fn main() {
let data = Arc::new("Hello, World".to_string());
let data_for_task_1 = data.clone();
tokio::task::spawn(async move {
println!("1: {}", data_for_task_1.len());
});
let data_for_task_2 = data.clone();
tokio::task::spawn(async move {
println!("2: {}", data_for_task_2.len());
});
}

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!

Waiting on multiple futures borrowing mutable self

Each of the following methods need (&mut self) to operate. The following code gives the error.
cannot borrow *self as mutable more than once at a time
How can I achieve this correctly?
loop {
let future1 = self.handle_new_connections(sender_to_connector.clone());
let future2 = self.handle_incoming_message(&mut receiver_from_peers);
let future3 = self.handle_outgoing_message();
tokio::pin!(future1, future2, future3);
tokio::select! {
_=future1=>{},
_=future2=>{},
_=future3=>{}
}
}
You are not allowed to have multiple mutable references to an object and there's a good reason for that.
Imagine you pass an object mutably to 2 different functions and they edited the object out of sync since you don't have any mechanism for that in place. then you'd end up with something called a race condition.
To prevent this bug rust allows only one mutable reference to an object at a time but you can have multiple immutable references and often you see people use internal mutability patterns.
In your case, you want data not to be able to be modified by 2 different threads at the same time so you'd wrap it in a Lock or RwLock then since you want multiple threads to be able to own this value you'd wrap that in an Arc.
here you can read about interior mutability in more detail.
Alternatively, while declaring the type of your function you could add proper lifetimes to indicate the resulting Future will be waited on in the same context by giving it a lifetime since your code waits for the future before the next iteration that would do the trick as well.
I encountered the same problem when dealing with async code. Here is what I figured out:
Let's say you have an Engine, that contains both incoming and outgoing:
struct Engine {
log: Arc<Mutex<Vec<String>>>,
outgoing: UnboundedSender<String>,
incoming: UnboundedReceiver<String>,
}
Our goal is to create two functions process_incoming and process_logic and then poll them simultaneously without messing up with the borrow checker in Rust.
What is important here is that:
You cannot pass &mut self to these async functions simultaneously.
Either incoming or outgoing will be only held by one function at most.
The data access by both process_incoming and process_logic need to be wrapped by a lock.
Any trying to lock Engine directly will lead to a deadlock at runtime.
So that leaves us giving up using the method in favor of the associated function:
impl Engine {
// ...
async fn process_logic(outgoing: &mut UnboundedSender<String>, log: Arc<Mutex<Vec<String>>>) {
loop {
Delay::new(Duration::from_millis(1000)).await.unwrap();
let msg: String = "ping".into();
println!("outgoing: {}", msg);
log.lock().push(msg.clone());
outgoing.send(msg).await.unwrap();
}
}
async fn process_incoming(
incoming: &mut UnboundedReceiver<String>,
log: Arc<Mutex<Vec<String>>>,
) {
while let Some(msg) = incoming.next().await {
println!("incoming: {}", msg);
log.lock().push(msg);
}
}
}
And we can then write main as:
fn main() {
futures::executor::block_on(async {
let mut engine = Engine::new();
let a = Engine::process_incoming(&mut engine.incoming, engine.log.clone()).fuse();
let b = Engine::process_logic(&mut engine.outgoing, engine.log).fuse();
futures::pin_mut!(a, b);
select! {
_ = a => {},
_ = b => {},
}
});
}
I put the whole example here.
It's a workable solution, only be aware that you should add futures and futures-timer in your dependencies.

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