How to take a reference to an outer variable in an FnMut closure?

I'm trying to borrow (not mutably) a variable in an FnMut closure. As soon as I have an await
in there, the compiler complains, even though I do everything I can to avoid having the reference
in scope accross the await point. Even adding an extra scope or cloning an Rc won't do...

I would like to avoid calling get_element_by_id in the loop. handle_send is a task that will be spawned.

error: captured variable cannot escape `FnMut` closure body
   --> src/entrypoint.rs:95:23
    |
95  |       stream.for_each( |_| async
    |  ________________________-_^
    | |                        |
    | |                        inferred to be a `FnMut` closure
96  | |     {
97  | |         let text;
98  | |
...   |
108 | |
109 | |     }).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

error: aborting due to previous error

This code runs in wasm. Ideally I wouldn't use the Rc. Note that nothing requires mutable access to textarea

async fn handle_send( on_clicks: impl Stream<Item=()>, mut out: impl Sink<String> + Unpin )
{
	let window   = web_sys::window  ().expect_throw( "no global `window` exists"              );
	let document = window  .document().expect_throw( "should have a document on window"       );
	let textarea = document.get_element_by_id( "chat_input" ).expect_throw( "find chat_input" );

	let ta = Rc::new( textarea );

	on_clicks.for_each( |_| async
	{
		let text;

		{
			let ta = ta.clone();

			// same error when just using textarea here.
			// 
			let text = ta.text_content();
			ta.set_text_content( None );
		}


		if let Some( t ) = text
		{
			// This line causes the error
			// 
			out.send( t ).await;
		}

	}).await;
}

Oops, it's the out variable that's causing trouble of course. To bad the compiler doesn't say which variable it's refering to.

Sorry, nevermind this question.

I just replaced the for_each with a while loop, which does not need a closure:

async fn handle_send( mut on_clicks: impl Stream<Item=()> + Unpin, mut out: impl Sink<String, Error=std::io::Error> + Unpin )
{
	let window   = web_sys::window  ().expect_throw( "no global `window` exists"              );
	let document = window  .document().expect_throw( "should have a document on window"       );
	let textarea = document.get_element_by_id( "chat_input" ).expect_throw( "find chat_input" );


	while on_clicks.next().await.is_some()
	{
		let text = textarea.text_content();
		textarea.set_text_content( None );

		if let Some( t ) = text
		{
			out.send( t ).await.expect_throw( "send line to server" );
		}
	};
}

Your out variable is the Future created by the async. It is confusing from lack of block and being on same line.
Try;

	on_clicks.for_each( move |_| { 
	    let ta = textarea.clone();
	    async move {

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.