Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't capture dynamic environment in a fn item

Tags:

rust

In this code everything works except task_id. I want this script to count requests in task_id:

use std::thread;
use std::thread::sleep_ms;
use std::sync::mpsc;
#[macro_use] extern crate nickel;
use nickel::Nickel;

fn main() {
    let mut server = Nickel::new();
    let mut task_id: i64 = 0;

    server.utilize(router! {
        get "**" => |_req, _res| {
            task_id += 1;
            run_heavy_task(task_id);
            "Yo!"
        }
    });

    server.listen("127.0.0.1:6767");
}

fn run_heavy_task(task_id: i64) {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        println!("heavy task {} started!", task_id);
        sleep_ms(3000);
        println!("heavy task {} completed", task_id);
        let result = tx.send(());
    });


    //rx.recv();
    //println!("Task {} completed", task_id);
}

error:

can't capture dynamic environment in a fn item; use the || { ... } closure form instead main.rs:13 task_id += 1;

Please help me solve this issue - how can I pass task_id into closure?

like image 486
OZ_ Avatar asked Apr 14 '15 00:04

OZ_


1 Answers

To expand on Chris Morgan's answer, this is a self-contained example with the same error:

fn main() {
    let mut a = 0;
    fn router() {
        a += 1;
    }
    router();
    println!("{}", a)
}

The problem is that fn items are not allowed to capture their environment, period. Capturing environment is non-trivial, and there are multiple ways to get variables into a closure. Closures are actually structs with the appropriate member variables for each captured variable.

Review Chris Morgan's statement:

Bear in mind how it may be multi-threaded; at the very least you will need to use some form of mutex to get it to work.

(Emphasis mine). You are creating a function, but you don't get to control how or when it is called. For all you know, Nickel may choose to call it from multiple threads - that's up to the library. As it is, you do not ever call the code task_id += 1!

I'm no expert on Nickel, but it appears that having dynamic routing is not possible with the code you've posted. However, it should be possible for you to avoid using the macro and construct a handler yourself, you "just" need to implement Middleware. That handler may be able to contain state, like your task_id.

like image 57
Shepmaster Avatar answered Sep 30 '22 04:09

Shepmaster