expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` in actix web - rust

I am using actix web and I am trying to return an async function in a closure but I am getting the following error:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server.rs:134:33
|
133 | ... web::get().to(
| -- the requirement to implement `Fn` derives from here
134 | / ... move |router: web::Data<Arc<Router>>,
135 | | ... headers: web::Data<Arc<Headers>>,
136 | | ... stream: web::Payload,
137 | | ... req: HttpRequest| async {
| |_________________________________________________________-
138 | || ... start_web_socket(req, stream, params).await
139 | || ... },
| || ^
| ||___________________________|
| |____________________________this closure implements `FnOnce`, not `Fn`
| closure is `FnOnce` because it moves the variable `params` out of its environment
error: aborting due to previous error; 1 warning emitted
This is the snippet that is resulting in the error. I have tried to document the code as much as I could.
I have tried moving out the variables in and out of the move blocks and have tried placing them at various places but without success.
What should be done instead of this?
pub fn start(
&mut self,
py: Python,
url: String,
port: u16,
socket: &PyCell<SocketHeld>,
name: String,
workers: usize,
) -> PyResult<()> {
if STARTED
.compare_exchange(false, true, SeqCst, Relaxed)
.is_err()
{
println!("Already running...");
return Ok(());
}
println!("{}", name);
let borrow = socket.try_borrow_mut()?;
let held_socket: &SocketHeld = &*borrow;
let raw_socket = held_socket.get_socket();
println!("Got our socket {:?}", raw_socket);
let router = self.router.clone();
let headers = self.headers.clone();
let directories = self.directories.clone();
let workers = Arc::new(workers);
let asyncio = py.import("asyncio").unwrap();
let event_loop = asyncio.call_method0("new_event_loop").unwrap();
asyncio
.call_method1("set_event_loop", (event_loop,))
.unwrap();
let event_loop_hdl = PyObject::from(event_loop);
thread::spawn(move || {
//init_current_thread_once();
actix_web::rt::System::new().block_on(async move {
let addr = format!("{}:{}", url, port);
println!("The number of workers are {}", workers.clone());
HttpServer::new(move || {
let mut app = App::new();
let event_loop_hdl = event_loop_hdl.clone();
let directories = directories.read().unwrap();
let router_copy = router.clone();
// this loop matches three types of directory serving
// 1. Serves a build folder. e.g. the build folder generated from yarn build
// 2. Shows file listing
// 3. Just serves the file without any redirection to sub links
for directory in directories.iter() {
if let Some(index_file) = &directory.index_file {
app = app.service(
Files::new(&directory.route, &directory.directory_path)
.index_file(index_file)
.redirect_to_slash_directory(),
);
} else if directory.show_files_listing {
app = app.service(
Files::new(&directory.route, &directory.directory_path)
.redirect_to_slash_directory()
.show_files_listing(),
);
} else {
app = app
.service(Files::new(&directory.route, &directory.directory_path));
}
}
app = app
.app_data(web::Data::new(router.clone()))
.app_data(web::Data::new(headers.clone()));
let web_socket_map = router_copy.get_web_socket_map().unwrap();
for elem in (web_socket_map).iter() {
let route = elem.key().clone();
let params = elem.value().clone();
app = app.route(
&route,
web::get().to(
move |router: web::Data<Arc<Router>>,
headers: web::Data<Arc<Headers>>,
stream: web::Payload,
req: HttpRequest| async {
start_web_socket(req, stream, params).await
},
),
)
}
app.default_service(web::route().to(move |router, headers, payload, req| {
pyo3_asyncio::tokio::scope_local(event_loop_hdl.clone(), async move {
index(router, headers, payload, req).await
})
}))
})
.keep_alive(KeepAlive::Os)
.workers(*workers.clone())
.client_timeout(0)
.listen(raw_socket.try_into().unwrap())
.unwrap()
.run()
.await
.unwrap();
});
});
----UPDATE----
Thank you for the suggestions. On updating the params, I was able to get rid of the original error but I am getting a similar but new error:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server.rs:134:33
|
133 | ... web::get().to(
| -- the requirement to implement `Fn` derives from here
134 | / ... |router: web::Data<Arc<Router>>,
135 | | ... headers: web::Data<Arc<Headers>>,
136 | | ... stream: web::Payload,
137 | | ... req: HttpRequest| async move {
| |_________________________________________________________-
138 | || ... start_web_socket(req, stream, params.clone()).await
139 | || ... },
| || ^
| ||___________________________|
| |____________________________this closure implements `FnOnce`, not `Fn`
| closure is `FnOnce` because it moves the variable `params` out of its environment
The new snippet looks like this:
let web_socket_map = router_copy.get_web_socket_map().unwrap();
for elem in (web_socket_map).iter() {
let route = elem.key().clone();
let params = elem.value().clone();
app = app.route(
&route,
web::get().to(
|router: web::Data<Arc<Router>>,
headers: web::Data<Arc<Headers>>,
stream: web::Payload,
req: HttpRequest| async move {
start_web_socket(req, stream, params.clone()).await
},
),
)
}
I have tried cloning params inside the async move but it still is giving the same error.

web::get() returns a Route, and web::get().to(...) is a Route method that expects a Handler. The Handler is expected to be an async function (async fn) - a function that returns a Future when called.
The problem is that in your code you are passing an async block, which IS a future.
An async block is a variant of a block expression which evaluates to a future.
So your code:
async move |...| { // Future
start_web_socket(req, stream, params.clone()).await
}
Could be converted to:
move |...| { // Handler Fn
async move { // Future
start_web_socket(req, stream, params.clone()).await
}
}
and that is equivalent to:
move |...| { // Handler Fn
start_web_socket(req, stream, params.clone()) // Future
}
because when you call an async fn start_web_socket without .await, you get a Future.
A tip for debugging such things is to assign things to intermediate variables, and checking the types that compiler deduces for them.

Related

Updating UI elements from a async loop

What is the standard method for updating UI elements from a loop calling a web request in Rust using gtk-rs? I am having trouble updating a label in a UI element to display some data from a web request.
Is there a standard way of doing this? The issues I have been having at the moment have all been related to passing data between threads and as such I am curious to know what is the common way of doing this?
I have a code example here where I get an error stating futures are not Send. I am using fragile in order to pass a box into the thread.
At the moment I am trying to solve the issue of making my futures Send however I am not sure that will solve the issue or just leave me with another similar problem.
use gtk::prelude::*;
use gtk::Orientation;
use adw::Application;
use std::thread;
use std::time::Duration;
use fragile;
const APP_ID: &str = "org.currency_trades";
fn main() {
let app = Application::builder().application_id(APP_ID).build();
app.connect_activate(build_ui);
app.run();
}
pub fn build_ui(app: &Application) -> gtk::Box {
let home_box = fragile::Fragile::new(gtk::Box::new(Orientation::Vertical, 15));
thread::spawn(move || {
let box_gbp = gtk::Box::new(Orientation::Horizontal, 250);
let gbp_label = gtk::Label::builder()
.label("GBP")
.margin_top(12)
.margin_start(50)
.build();
let gbp_price_label = gtk::Label::builder()
.label("Uninitialized")
.margin_top(12)
.margin_end(50)
.build();
box_gbp.append(&gbp_label);
box_gbp.append(&gbp_price_label);
home_box.get().append(&box_gbp);
loop {
let runtime = tokio::runtime::Runtime::new().unwrap();
std::thread::sleep(Duration::from_millis(1000));
let _ = runtime.block_on(runtime.spawn(async move {
let gbp_label_request = match reqwest::get("https://www.boredapi.com/api/activity").await {
Ok(label) => label,
Err(_) => panic!("Panic!")
};
let gbp_label = match gbp_label_request.text().await {
Ok(r) => r,
Err(_) => String::from("Unknown")
};
gbp_price_label.set_label(&gbp_label);
}));
}
});
return *home_box.get();
}
The associated error:
error: future cannot be sent between threads safely
--> src/main.rs:89:52
|
89 | let _ = runtime.block_on(runtime.spawn(async move {
| ____________________________________________________^
90 | | let gbp_label_request = match reqwest::get("https://www.boredapi.com/api/activity").await {
91 | | Ok(label) => label,
92 | | Err(_) => panic!("Panic!")
... |
100 | | gbp_price_label.set_label(&gbp_label);
101 | | }));
| |_____________^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `*mut c_void`
note: captured value is not `Send`
--> src/main.rs:100:17
|
100 | gbp_price_label.set_label(&gbp_label);
| ^^^^^^^^^^^^^^^ has type `gtk4::Label` which is not `Send`
note: required by a bound in `Runtime::spawn`
--> /Users/andy/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs:192:21
|
192 | F: Future + Send + 'static,
| ^^^^ required by this bound in `Runtime::spawn`
For more information about this error, try `rustc --explain E0271`.
There is an example in the gtk-rs event loop documentation that explains how to do this using MainContext::channel.
let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT);
// Connect to "clicked" signal of `button`
button.connect_clicked(move |_| {
let sender = sender.clone();
// The long running operation runs now in a separate thread
thread::spawn(move || {
// Deactivate the button until the operation is done
sender.send(false).expect("Could not send through channel");
let ten_seconds = Duration::from_secs(10);
thread::sleep(ten_seconds);
// Activate the button again
sender.send(true).expect("Could not send through channel");
});
});
// The main loop executes the closure as soon as it receives the message
receiver.attach(
None,
clone!(#weak button => #default-return Continue(false),
move |enable_button| {
button.set_sensitive(enable_button);
Continue(true)
}
),
);

Rust declare first assign later pattern

I have a bi-directional grpc stream that acts as bridge to a kafka cluster. When the stream is first initialised, I was to create the kafka consumer and start using it.
To do so, I thought of initialising an empty consumer, waiting for the first input, then assigning a created consumer to an empty one. I tried to do so by following the pattern here.
https://doc.rust-lang.org/rust-by-example/variable_bindings/declare.html
Rust is throwing a possibly-unitialized variable error, is this because it is being initialised in an asynchronous stream?
use std::pin::Pin;
use futures::{Stream, StreamExt};
use kafka::consumer::{Consumer, FetchOffset, GroupOffsetStorage};
use tonic::transport::Server;
use tonic::{Request, Response, Status};
use bridge::kafka_stream_server::{KafkaStream, KafkaStreamServer};
use bridge::{KafkaResponse, PublishRequest};
pub mod bridge {
tonic::include_proto!("bridge"); // The string specified here must match the proto package name
}
#[derive(Default)]
pub struct KafkaStreamService {}
pub fn create_kafka_consumer(topic: String) -> Consumer {
Consumer::from_hosts(vec!["localhost:9092".to_owned()])
.with_topic(topic.to_owned())
.with_fallback_offset(FetchOffset::Latest)
.with_group("".to_owned())
.with_offset_storage(GroupOffsetStorage::Kafka)
.create()
.unwrap()
}
#[tonic::async_trait]
impl KafkaStream for KafkaStreamService {
type SubscribeStream =
Pin<Box<dyn Stream<Item = Result<KafkaResponse, Status>> + Send + Sync + 'static>>;
async fn subscribe(
&self,
request: Request<tonic::Streaming<PublishRequest>>,
) -> Result<Response<Self::SubscribeStream>, Status> {
println!("Initiated stream!");
let mut stream = request.into_inner();
let mut consumer_created_flag: bool = false;
let consumer: Consumer; //declared here
let output = async_stream::try_stream! {
while let Some(publication) = stream.next().await {
let message = publication?;
let topic = message.topic.clone();
if consumer_created_flag == false {
consumer = create_kafka_consumer(topic); //error occurs here
consumer_created_flag = true;
}
let reply = bridge::KafkaResponse {
content: format!("Hello {}!", "world"),
};
yield reply.clone();
}
};
Ok(Response::new(Box::pin(output) as Self::SubscribeStream))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse().unwrap();
println!("KafkaService listening on: {}", addr);
let svc = KafkaStreamServer::new(KafkaStreamService::default());
Server::builder().add_service(svc).serve(addr).await?;
Ok(())
}
EDIT: verbose error as requested:
error[E0381]: use of possibly-uninitialized variable: `consumer`
--> src/server.rs:42:22
|
42 | let output = async_stream::try_stream! {
| ______________________^
43 | | while let Some(publication) = stream.next().await {
44 | | let message = publication?;
45 | | let topic = message.topic.clone();
46 | | if consumer_created_flag == false {
47 | | consumer = create_kafka_consumer(topic);
| | -------- use occurs due to use in generator
... |
54 | | }
55 | | };
| |_________^ use of possibly-uninitialized `consumer`
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
The declare first pattern only works with basic control flow (if, match, {}, etc). It falls apart when referenced or moved into another object, like an async block or a closure:
fn main() {
let val: i32;
let func = move || {
val = 5;
};
}
error[E0594]: cannot assign to `val`, as it is not declared as mutable
--> src/main.rs:4:9
|
2 | let val: i32;
| --- help: consider changing this to be mutable: `mut val`
3 | let func = move || {
4 | val = 5;
| ^^^^^^^ cannot assign
error[E0381]: use of possibly-uninitialized variable: `val`
--> src/main.rs:3:16
|
3 | let func = move || {
| ^^^^^^^ use of possibly-uninitialized `val`
4 | val = 5;
| --- use occurs due to use in closure
A potential fix is to move its declaration into the try_stream! macro:
let output = async_stream::try_stream! {
let mut consumer_created_flag: bool = false;
let consumer: Consumer;
while let Some(publication) = stream.next().await {
let message = publication?;
let topic = message.topic.clone();
if consumer_created_flag == false {
consumer = create_kafka_consumer(topic);
consumer_created_flag = true;
}
let reply = KafkaResponse {
content: format!("Hello {}!", "world"),
};
yield reply.clone();
}
};
However, this causes a new error because you're potentially assigning to it twice (the compiler doesn't know that consumer_created_flag is guarding it):
error[E0384]: cannot assign twice to immutable variable `consumer`
--> src\lib.rs:1348:21
|
44 | let consumer: Consumer; //declared here
| -------- help: make this binding mutable: `mut consumer`
...
49 | consumer = create_kafka_consumer(topic); //error occurs here
| ^^^^^^^^ cannot assign twice to immutable variable
Fortunately a quick fix is to simply make consumer mutable. And then the only thing the compiler complains about is that it is unused, but I figure there's a reason you've put it there.

Why does i got lifetime error in rust closure

I'm testing some rust wasm features, and have some problem with closures.
I'm implemented this function, which setup callback on button click event.
pub fn setup_click(&mut self) {
let mut clicks = 0;
let ws_cloned = self.websocket.clone();
let num_clicks_cloned = self.num_clicks.clone();
let notifications = Rc::new(RefCell::new(Notificator::new(
NotificationConfig::new_default(),
)));
let cb = move |_: Event| {
clicks += 1;
num_clicks_cloned
.borrow_mut()
.set_inner_html(clicks.to_string());
let mut map: Map<String, Value> = serde_json::Map::new();
map.insert("key".to_string(), Value::String(clicks.to_string()));
if let Ok(ws) = ws_cloned.clone().try_borrow_mut() {
ws.send_rpc(
String::from("click"),
Params::Map(map),
Box::new(|payload: String| {
notifications.clone().borrow_mut().display(
payload,
"Click success".to_string(),
"success".to_string(),
)
}),
);
}
};
self.click_button.add_event_listener("click", cb);
}
where third param of the ws.send rpc is
pub type RPCHandler = Box<dyn Fn(String) + 'static>;
and add_event_listener has this sugnature
pub fn add_event_listener<T>(&mut self, event_name: &str, handler: T)
where
T: 'static + FnMut(web_sys::Event),
{
let cb = Closure::wrap(Box::new(handler) as Box<dyn FnMut(_)>);
if let Some(el) = self.el.take() {
let el_et: EventTarget = el.into();
el_et
.add_event_listener_with_callback(event_name, cb.as_ref().unchecked_ref())
.unwrap();
cb.forget();
if let Ok(el) = el_et.dyn_into::<web_sys::Element>() {
self.el = Some(el);
}
}
}
When i try to compile the code i got life time error
--> src/test_click_btn.rs:46:21
|
35 | let cb = move |_: Event| {
| --------------- lifetime `'1` represents this closure's body
...
46 | / Box::new(|payload: String| {
47 | | notifications.clone().borrow_mut().display(
48 | | payload,
49 | | "Click success".to_string(),
50 | | "success".to_string(),
51 | | )
52 | | }),
| |______________________^ cast requires that `'1` must outlive `'static`
|
= note: closure implements `FnMut`, so references to captured variables can't escape the closure```
I see that notifications not live long enough, but can't understand how to fix this error)
There's no guarantee in this code that the closure passed to send_rpc will last no longer than the event callback closure. Therefore, it needs to be made a move closure too, so that it can live independently rather than borrowing from the event handler closure.
Conveniently, you already have notifications wrapped in Rc, which is just what you need, but you've performed the clone in the wrong place. This line
notifications.clone().borrow_mut().display(
performs a clone and dereferences it immediately, so it's redundant. Instead, you need to clone it before creating the closure so that the closure (now move) can own it:
let notifications = notifications.clone(); // create a clone that will be moved into the closure
ws.send_rpc(
String::from("click"),
Params::Map(map),
Box::new(move |payload: String| { // now a move closure
notifications.borrow_mut().display( // no clone here
...

How can I use Rust with wasm-bindgen to create a closure that creates another closure with state?

I am trying to create a small web application that will allow the user to drag and drop files onto the window. The files will then be read and their contents printed along with their filenames to the console. In addition, the files will be added to a list.
The equivalent code in JS could look something like:
window.ondragenter = (e) => {
e.preventDefault();
}
window.ondragover = (e) => {
e.preventDefault();
}
const allFiles = [];
const dropCallback = (e) => {
e.preventDefault();
const files = e.dataTransfer.files;
console.log("Got", files.length, "files");
for (let i = 0; i < files.length; i++) {
const file = files.item(i);
const fileName = file.name;
const readCallback = (text) => {
console.log(fileName, text);
allFiles.push({fileName, text});
}
file.text().then(readCallback);
}
};
window.ondrop = dropCallback;
When trying to do this in Rust, I run in to the problem that the outer closure needs to implement FnOnce to move all_files out of its scope again, which breaks the expected signature for Closure::wrap. And Closure::once will not do the trick, since I need to be able to drop multiple files onto the window.
Here is the code that I have tried without luck:
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
macro_rules! console_log {
($($t:tt)*) => (web_sys::console::log_1(&JsValue::from(format_args!($($t)*).to_string())))
}
struct File {
name: String,
contents: String,
}
#[wasm_bindgen]
pub fn main() {
let mut all_files = Vec::new();
let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
event.prevent_default();
let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event);
let drag_event = drag_event_ref.clone();
match drag_event.data_transfer() {
None => {}
Some(data_transfer) => match data_transfer.files() {
None => {}
Some(files) => {
console_log!("Got {:?} files", files.length());
for i in 0..files.length() {
if let Some(file) = files.item(i) {
let name = file.name();
let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
let contents = text.as_string().unwrap();
console_log!("Contents of {:?} are {:?}", name, contents);
all_files.push(File {
name,
contents
});
}) as Box<dyn FnMut(JsValue)>);
file.text().then(&read_callback);
read_callback.forget();
}
}
}
},
}
}) as Box<dyn FnMut(&web_sys::Event)>);
// These are just necessary to make sure the drop event is sent
let drag_enter = Closure::wrap(Box::new(|event: &web_sys::Event| {
event.prevent_default();
console_log!("Drag enter!");
}) as Box<dyn FnMut(&web_sys::Event)>);
let drag_over = Closure::wrap(Box::new(|event: &web_sys::Event| {
event.prevent_default();
console_log!("Drag over!");
}) as Box<dyn FnMut(&web_sys::Event)>);
// Register all the events on the window
web_sys::window()
.and_then(|win| {
win.set_ondragenter(Some(JsCast::unchecked_from_js_ref(drag_enter.as_ref())));
win.set_ondragover(Some(JsCast::unchecked_from_js_ref(drag_over.as_ref())));
win.set_ondrop(Some(JsCast::unchecked_from_js_ref(drop_callback.as_ref())));
win.document()
})
.expect("Could not find window");
// Make sure our closures outlive this function
drag_enter.forget();
drag_over.forget();
drop_callback.forget();
}
The error I get is
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
--> src/lib.rs:33:72
|
33 | ... let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
| - ^^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
| _________________________________________________________|
| |
34 | | ... let contents = text.as_string().unwrap();
35 | | ... console_log!("Contents of {:?} are {:?}", name, contents);
36 | | ...
37 | | ... all_files.push(File {
38 | | ... name,
| | ---- closure is `FnOnce` because it moves the variable `name` out of its environment
39 | | ... contents
40 | | ... });
41 | | ... }) as Box<dyn FnMut(JsValue)>);
| |________________________- the requirement to implement `FnMut` derives from here
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
--> src/lib.rs:20:48
|
20 | let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
| - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
| _______________________________________|
| |
21 | | event.prevent_default();
22 | | let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event);
23 | | let drag_event = drag_event_ref.clone();
... |
33 | | let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
| | -------------------- closure is `FnOnce` because it moves the variable `all_files` out of its environment
... |
50 | | }
51 | | }) as Box<dyn FnMut(&web_sys::Event)>);
| |______- the requirement to implement `FnMut` derives from here
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0525`.
error: could not compile `hello_world`.
To learn more, run the command again with --verbose.
In a more complex example that I have not been able to reproduce in a simpler form, I get a more cryptic error, but I expect it to be related to the above:
error[E0277]: expected a `std::ops::FnMut<(&web_sys::Event,)>` closure, found `[closure#src/main.rs:621:52: 649:10 contents:std::option::Option<std::string::String>, drop_proxy:winit::event_loop::EventLoopProxy<CustomEvent>]`
--> src/main.rs:621:43
|
621 | let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
| ___________________________________________^
622 | | event.prevent_default();
623 | | let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event);
624 | | let drag_event = drag_event_ref.clone();
... |
648 | | }
649 | | }) as Box<dyn FnMut(&web_sys::Event)>);
| |__________^ expected an `FnMut<(&web_sys::Event,)>` closure, found `[closure#src/main.rs:621:52: 649:10 contents:std::option::Option<std::string::String>, drop_proxy:winit::event_loop::EventLoopProxy<CustomEvent>]`
I tried putting the all_files variable into a RefCell, but I still got a similar error. Are there any tricks or types that I can use to work around this in Rust and achieve what I want?
First, you are trying to copy name into a number of instances of File, but it must be cloned. Second, you need to properly ensure that all_files will be available whenever a closure wants to call it. One way to do so is by using a RefCell to enable multiple closures to write to it, and wrapping that in a Rc to ensure that it stays alive as long as any of the closures are alive.
Try this:
use std::{cell::RefCell, rc::Rc};
use wasm_bindgen::{prelude::*, JsCast, JsValue};
macro_rules! console_log {
($($t:tt)*) => (web_sys::console::log_1(&JsValue::from(format_args!($($t)*).to_string())))
}
struct File {
name: String,
contents: String,
}
#[wasm_bindgen]
pub fn main() {
let all_files = Rc::new(RefCell::new(Vec::new()));
let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
event.prevent_default();
let drag_event_ref: &web_sys::DragEvent = event.unchecked_ref();
let drag_event = drag_event_ref.clone();
match drag_event.data_transfer() {
None => {}
Some(data_transfer) => match data_transfer.files() {
None => {}
Some(files) => {
console_log!("Got {:?} files", files.length());
for i in 0..files.length() {
if let Some(file) = files.item(i) {
let name = file.name();
let all_files_ref = Rc::clone(&all_files);
let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
let contents = text.as_string().unwrap();
console_log!("Contents of {:?} are {:?}", &name, contents);
(*all_files_ref).borrow_mut().push(File {
name: name.clone(),
contents,
});
})
as Box<dyn FnMut(JsValue)>);
file.text().then(&read_callback);
read_callback.forget();
}
}
}
},
}
}) as Box<dyn FnMut(&web_sys::Event)>);
// These are just necessary to make sure the drop event is sent
let drag_enter = Closure::wrap(Box::new(|event: &web_sys::Event| {
event.prevent_default();
console_log!("Drag enter!");
}) as Box<dyn FnMut(&web_sys::Event)>);
let drag_over = Closure::wrap(Box::new(|event: &web_sys::Event| {
event.prevent_default();
console_log!("Drag over!");
}) as Box<dyn FnMut(&web_sys::Event)>);
// Register all the events on the window
web_sys::window()
.and_then(|win| {
win.set_ondragenter(Some(drag_enter.as_ref().unchecked_ref()));
win.set_ondragover(Some(drag_over.as_ref().unchecked_ref()));
win.set_ondrop(Some(drop_callback.as_ref().unchecked_ref()));
win.document()
})
.expect("Could not find window");
// Make sure our closures outlive this function
drag_enter.forget();
drag_over.forget();
drop_callback.forget();
}
Note that if you are using multiple threads, you may want something other than RefCell (maybe Mutex instead). Also, I also changed uses of JsCast::unchecked_from_js_ref(x) to the more canonical x.as_ref().unchecked_ref().

Error on Future generator closure: Captured variable cannot escape `FnMut` closure body

I want to create a simple websocket server. I want to process the incoming messages and send a response, but I get an error:
error: captured variable cannot escape `FnMut` closure body
--> src\main.rs:32:27
|
32 | incoming.for_each(|m| async {
| _________________________-_^
| | |
| | inferred to be a `FnMut` closure
33 | | match m {
34 | | // Error here...
35 | | Ok(message) => do_something(message, db, &mut outgoing).await,
36 | | Err(e) => panic!(e)
37 | | }
38 | | }).await;
| |_____^ returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
This gives a few hits on Stack Overflow but I don't see anywhere in my code where a variable is escaping. The async block won't run concurrently, so I don't see any problem. Furthermore, I feel like I am doing something very simple: I get a type which allows me to send data back to the client, but when using a reference to it in the async block, it gives a compile error. The error only occurs when I use the outgoing or db variable in the async code.
This is my code (error is in the handle_connection function):
main.rs
use tokio::net::{TcpListener, TcpStream};
use std::net::SocketAddr;
use std::sync::Arc;
use futures::{StreamExt, SinkExt};
use tungstenite::Message;
use tokio_tungstenite::WebSocketStream;
struct DatabaseConnection;
#[tokio::main]
async fn main() -> Result<(), ()> {
listen("127.0.0.1:3012", Arc::new(DatabaseConnection)).await
}
async fn listen(address: &str, db: Arc<DatabaseConnection>) -> Result<(), ()> {
let try_socket = TcpListener::bind(address).await;
let mut listener = try_socket.expect("Failed to bind on address");
while let Ok((stream, addr)) = listener.accept().await {
tokio::spawn(handle_connection(stream, addr, db.clone()));
}
Ok(())
}
async fn handle_connection(raw_stream: TcpStream, addr: SocketAddr, db: Arc<DatabaseConnection>) {
let db = &*db;
let ws_stream = tokio_tungstenite::accept_async(raw_stream).await.unwrap();
let (mut outgoing, incoming) = ws_stream.split();
// Adding 'move' does also not work
incoming.for_each(|m| async {
match m {
// Error here...
Ok(message) => do_something(message, db, &mut outgoing).await,
Err(e) => panic!(e)
}
}).await;
}
async fn do_something(message: Message, db: &DatabaseConnection, outgoing: &mut futures_util::stream::SplitSink<WebSocketStream<TcpStream>, Message>) {
// Do something...
// Send some message
let _ = outgoing.send(Message::Text("yay".to_string())).await;
}
Cargo.toml
[dependencies]
futures = "0.3.*"
futures-channel = "0.3.*"
futures-util = "0.3.*"
tokio = { version = "0.2.*", features = [ "full" ] }
tokio-tungstenite = "0.10.*"
tungstenite = "0.10.*"
When using async move, I get the following error:
code
incoming.for_each(|m| async move {
let x = &mut outgoing;
let b = db;
}).await;
error
error[E0507]: cannot move out of `outgoing`, a captured variable in an `FnMut` closure
--> src\main.rs:33:38
|
31 | let (mut outgoing, incoming) = ws_stream.split();
| ------------ captured outer variable
32 |
33 | incoming.for_each(|m| async move {
| ______________________________________^
34 | | let x = &mut outgoing;
| | --------
| | |
| | move occurs because `outgoing` has type `futures_util::stream::stream::split::SplitSink<tokio_tungstenite::WebSocketStream<tokio::net::tcp::stream::TcpStream>, tungstenite::protocol::message::Message>`, which does not implement the `Copy` trait
| | move occurs due to use in generator
35 | | let b = db;
36 | | }).await;
| |_____^ move out of `outgoing` occurs here
FnMut is an anonymous struct, since FnMutcaptured the &mut outgoing, it becomes a field inside of this anonymous struct and this field will be used on each call of FnMut , it can be called multiple times. If you lose it somehow (by returning or moving into another scope etc...) your program will not able to use that field for further calls, due to safety Rust Compiler doesn't let you do this(for your both case).
In your case instead of capturing the &mut outgoing we can use it as argument for each call, with this we'll keep the ownership of outgoing. You can do this by using fold from futures-rs:
incoming
.fold(outgoing, |mut outgoing, m| async move {
match m {
// Error here...
Ok(message) => do_something(message, db, &mut outgoing).await,
Err(e) => panic!(e),
}
outgoing
})
.await;
This may seem a bit tricky but it does the job, we are using constant accumulator(outgoing) which will be used as an argument for our FnMut.
Playground (Thanks #Solomon Ucko for creating reproducible example)
See also :
How to return the captured variable from `FnMut` closure, which is a captor at the same time
How can I move a captured variable into a closure within a closure?

Resources