Search code examples
closuresrustspawndecouplinglifetime

What is the best way to decouple a caller of spawn from the spawned procedure?


Rust 0.12.

Let's say I have a sendable closure, that is defined entirely separately from the code that should spawn it in another task.

Closure type:

type closure_type = ||: 'static + Send;

Code:

let do_stuff : closure_type = || println!("stuff"); // "uknown" procedure
let outer_fun = || {
    spawn(proc() {
        do_stuff();
    });
};
outer_fun(); // someone else calls this

The above example fails to compile with a message "can not move out of captured outer variable" at the "proc()" scope. Why does the outer_fun captures do_stuff variable if it is not used there?

After tinkering a bit, I found that one possible solution would be to explicitly pass do_stuff function to outer_fun closure (this compiles):

let do_stuff : closure_type = || println!("stuff");
let outer_fun = | do_do_stuff: closure_type | {
    spawn(proc() {
        do_do_stuff();
    });
};
outer_fun(do_stuff); // I don't want someone else to know about "do_stuff"!

But this closes the possibility of someone else invoking the spawn without knowing what it spawns.

Obviously, my brain might be a bit damaged by unconstrained consumption of Javascript, so I must humbly ask: what would be a correct way to accomplish something like this in Rust?

UPDATE: Just spawn a proc on the main callback! Duh.

let do_stuff = || spawn(proc() println!("Hello"));
let do_spawn = || {
    do_stuff();
};
for i in range(1u, 10) {
    do_spawn(); // Even multiple times.
}

Solution

  • Why does the outer_fun captures do_stuff variable if it is not used there?

    But it is used there! do_stuff appears inside closure body, and closures are lexical, so do_stuff is necessarily captured by outer_fun.

    Currently closures capture every variable by reference. This is useful because it allows you to mutate "outer" variables as if the closure body were embedded directly in the outer code. However, this does preclude you from moving out of captured variables (because it would mean moving out of a reference).

    proc()s, on the other hand, have their environment on the heap, and they are callable only once. It means that they move every captured value into their environment. However, you're trying to use do_stuff (which was captured by the outer_fun closure) inside proc() body, which is captured by reference and cannot be moved into proc(), so here is your error.

    I don't think it is possible to do what you want to yet. Currently there is an accepted unboxed closures proposal. Proper unboxed closures will soon be implemented; in particular unboxed closures can capture their environment by value, just like proc()s (which will be superseded by explicitly boxed unboxed closures [no pun intended]). When unboxed closures land, as far as I understand, you'll be able to compile your first example exactly as it is (only changing proc() to box || or something).