Captured variable cannot escape `FnMut` closure body 2

how can I move a websocket message from tungstenite into FnMut()?

 read.for_each(|message| async {

            match message.unwrap() {

                Message::Text(message) => {
                    
                    if let Ok(message) = serde_json::from_str::<Test>(&message) {

                        callback(&message); // cant borrow or move it into here
                    };
                },
                Message::Ping(message) => { println!("{} ping", String::from_utf8(message).unwrap()); },
                Message::Close(x) => { dbg!("closed"); },
                _ => println!("not implemented")
            };

        });

The problem is here that I'm trying to call FnMut inside an FnMut async. Which is the usual workaround for this? Should something like Arcbe used?

You'll probably have to supply more context, say,

  • the full error from cargo check (not your IDE)
  • the entire function body (so one can figure out the types of nothing else)

I'm trying to make some API client lib crate to learn Rust. I already wrote this in Typescript, but I can't apply most concepts into Rust. It is a whole different deal. Working with closures, as I can see here is difficult.

async fn agg_trade<C>(&mut self, callback: C, symbol:&str) -> Result<(), Box<dyn std::error::Error>> where C: FnMut(binance::ws::AggTrade) {

        let Websocket { ref mut connections, .. } = self;

        let (stream, response) = 
        connect_async("wss://fstream.binancefuture.com/ws/xrpusdt@aggTrade")
        .await
        .expect("connect_async_error");

        connections.insert(String::from(symbol), Some(()));

        let (write, read) = stream.split();

        let _ = read.for_each(|message| async {

            match message.unwrap() {

                Message::Text(message) => {
                    
                    if let Ok(message) = serde_json::from_str::<binance::ws::AggTrade>(&message) {

                        callback(message);

                        /* match message {
            
                            binance::ws::Events::Result(e) => binance::ws::Events::Result(e),
                            binance::ws::Events::Error(e) => binance::ws::Events::Error(e),
                            binance::ws::Events::BookTicker(e) => binance::ws::Events::BookTicker(e),
                            binance::ws::Events::AggTrade(e) => binance::ws::Events::AggTrade(e),
                            binance::ws::Events::None => binance::ws::Events::None,
                        }; */
                    };
                },
                Message::Ping(message) => { println!("{} ping", String::from_utf8(message).unwrap()); },
                Message::Close(x) => { dbg!("closed"); },
                _ => println!("not implemented")
            };

        });

        Ok(())

    } // <symbol>@aggTrade

And the full error message?

Shots in the dark, but does callback(mesaage.to_owned()) or async move help?

captured variable cannot escape `FnMut` closure body
`FnMut` closures only have access to their captured variables while they are executing...
...therefore, they cannot allow references to captured variables to escape
the method `to_owned` exists for struct `AggTrade`, but its trait bounds were not satisfied
the following trait bounds were not satisfied:
`ws::AggTrade: Clone`
which is required by `ws::AggTrade: ToOwned`
move occurs because `callback` has type `C`, which does not implement the `Copy` trait

When using move:

cannot move out of `callback`, a captured variable in an `FnMut` closure
`callback` is moved here

Those are not the full error messages. Run cargo check and copy that output. They have pointers to specific parts of the source code which usually make the messages make a lot more sense (to us and you).

I'm trying to give the user an Interface like that:

let close_ws = ws.agg_trade(|event:AggTrade| {

        // dbg!(event);

    }, "SYMBOL").await;

but it seems it's not that simple in Rust.

When you post errors or code, post text, not screenshots.

You can try some variations of

move |message| async move { ... }

I tried to figure out your types but couldn't (I found a few AggTrade crates but they all implemented Clone). Including your dependency list or type definitions may help.

1 Like

Thanks, unfortunately move doesn't work either:

     |
1242 |       async fn agg_trade<C>(&mut self, mut callback: C, symbol:&str) -> Result<(), Box<dyn std::error::Error>> where C: FnMut() {
     |                                        ------------ captured outer variable
...
1266 |           let _ = read.for_each(move |message| async move {
     |  _______________________________--------------_^
     | |                               |
     | |                               captured by this `FnMut` closure
1267 | |
1268 | |             let _ = match message.unwrap() {
1269 | |
...    |
1274 | |                         callback();
     | |                         --------
     | |                         |
     | |                         variable moved due to use in coroutine
     | |                         move occurs because `callback` has type `C`, which does not implement the `Copy` trait
...    |
1291 | |
1292 | |         });
     | |_________^ `callback` is moved here

These are the Types

        #[derive(serde::Deserialize, Debug)]
        pub struct AggTrade {

            #[serde(rename="e")] event_type: String,
            #[serde(rename="E")] event_time: u64,
            #[serde(rename="s")] symbol: String,
            #[serde(rename="a")] trade_id: u64,
            #[serde(rename="p")] price: String,
            #[serde(rename="q")] quantity: String,
            #[serde(rename="f")] first_trade_id: u64,
            #[serde(rename="l")] last_trade_id: u64,
            #[serde(rename="T")] trade_time: u64,
            #[serde(rename="m")] maker: bool,
        }

        #[derive(serde::Deserialize, Debug)]
        #[serde(untagged)]
        pub enum Events {

            Result(Result),
            Error(Error),
            AggTrade(AggTrade),
            BookTicker(BookTicker),
            None,
        }