Call closure: can't capture dynamic environment in a fn item

I'm trying to call a method that inside in using Tokio to do a request and return the result to my callback.
Since I'm doing it in the main I'm trying to add CondVar to block it until the request is done, but I got next error which I don't knot how to fix:

error[E0434]: can't capture dynamic environment in a fn item
  --> src/main.rs:42:34
   |
42 |             let (lock, cvar) = &*con_var_send;
   |                                  ^^^^^^^^^^^^
   |
   = help: use the `|| { ... }` closure form instead

I tried this:

 //for thread sleep
use std::sync::{Arc, Mutex, Condvar};

//My network client
mod swapi;
use swapi::SwapiCallback;

//used in swapi client
#[macro_use]
extern crate lazy_static;

//measure time
use std::time::Instant;
//DTO
use crate::swapi::People;

fn main() {
    //barrier
    let con_var = Arc::new((Mutex::new(true), Condvar::new()));
    let con_var_send = con_var.clone();

    //Create callback
    struct Callback {
        start: Instant,
    }
    impl Callback {
        fn new() -> Callback {
            Callback {
                start: Instant::now()
            }
        }
    }
    unsafe impl Send for Callback {} //require to share it between threads
    impl SwapiCallback for Callback {
        #[allow(non_snake_case)]
        fn onLoad(&self, s: Vec<People>) {
            let diff = self.start.elapsed().as_millis();
            println!("Request: count {}; duration: {}", s.len(), diff);
            //unlock thread
            //notify lock that thread finished work
            let (lock, cvar) = &*con_var_send;
            let mut started = lock.lock().unwrap();
            *started = true;
            // We notify the condvar that the value has changed.
            cvar.notify_one();
        }

        #[allow(non_snake_case)]
        fn onError(&self, s: &str) {
            println!("Error: {:#?}", s);
        }
    }
    //call swapi client
    let client = swapi::SwapiClient::new();
    client.loadAllPeople(Box::new(Callback::new()));

    //wait for thread to finish
    // Wait for the thread to start up.
    let (lock, cvar) = &*con_var;
    let mut started = lock.lock().unwrap();
    while !*started {
        started = cvar.wait(started).unwrap();
    }
    println!("Main finished")
}

As well I tried to make callback to the struct:

fn main() {
    //barrier
    let con_var = Arc::new((Mutex::new(true), Condvar::new()));
    let con_var_send = con_var.clone();

    let unlock = move || {
        let (lock, cvar) = &*con_var_send;
        let mut started = lock.lock().unwrap();
        *started = true;
        // We notify the condvar that the value has changed.
        cvar.notify_one();
    };

    //Create callback
    struct Callback {
        start: Instant,
        unlock: Box<dyn FnMut()>,
    }
    impl Callback {
        fn new(unlock: Box<dyn FnMut()>) -> Callback {
            Callback {
                start: Instant::now(),
                unlock: unlock
            }
        }
    }
    unsafe impl Send for Callback {} //require to share it between threads
    impl SwapiCallback for Callback {
        #[allow(non_snake_case)]
        fn onLoad(&self, s: Vec<People>) {
            let diff = self.start.elapsed().as_millis();
            println!("Request: count {}; duration: {}", s.len(), diff);
            //unlock thread
            //notify lock that thread finished work
            (self.unlock)();
        }

        #[allow(non_snake_case)]
        fn onError(&self, s: &str) {
            println!("Error: {:#?}", s);
        }
    }
    //call swapi client
    let client = swapi::SwapiClient::new();
    client.loadAllPeople(Box::new(Callback::new(Box::new(unlock))));

    //wait for thread to finish
    // Wait for the thread to start up.
    let (lock, cvar) = &*con_var;
    let mut started = lock.lock().unwrap();
    while !*started {
        started = cvar.wait(started).unwrap();
    }
    println!("Main finished")
}

But here I got another error:

error[E0596]: cannot borrow `self.unlock` as mutable, as it is behind a `&` reference
  --> src/main.rs:48:13
   |
43 |         fn onLoad(&self, s: Vec<People>) {
   |                   ----- help: consider changing this to be a mutable reference: `&mut self`
...
48 |             (self.unlock)();
   |             ^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable

What is correct way to do it?
If I would had thread::spawn and move inside it would work, why it is not ho to handle this with struct?

The error in this case is that you need mutable access to call an FnMut, but your onLoad method only takes &self, so it only has immutable access.

1 Like

This one I can fix.. but in general - what about the first example?

can't capture dynamic environment in a fn item

Is it possible to avoid sending Fn to Struct and use closure from main directly?

No, you need to move the data by making it a field in the struct. The code might become clearer if you define your struct outside of the main function.

1 Like

Thanks, understood,
It there any other option to declare closure or implement trait without struct?

I want to achieve something similar to Java anonymous class. I already read that I cannot do it directly, but maybe there are some hacks?

No, a closure only implements the function traits. You have already found the methods available to you.

1 Like

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