Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the rust compiler "error: can't capture dynamic environment in a fn item; use the || { ... } closure form instead" mean, and how to fix it?

Tags:

syntax

rust

I get a rust compiler error:

src/main.rs:33:31: 33:35 error: can't capture dynamic environment in a fn item; use the || { ... } closure form instead

The error occurs because I have a function in which I declare a variable using let, and then have an inner function in which I try to use this variable that is closed over.

I am fairly certain that this is a beginner question, so sorry in advance if this has a very straight forward answer!

Note that I am using this inner function as a callback somewhere, and thus using a closure, like

let closure_fn = | args | -> () { do stuff };

... is not going to be an appropriate solution for me.


extern crate nickel;

use std::io::net::ip::Ipv4Addr;
use nickel::{ Nickel, Request, Response };

fn stub_3rd_party_function() -> String {
    "hello world".to_string()
}

fn main() {

    let mut server = Nickel::new();

    // assume that the variable **must** be instantiated like this
    let hello_text : String = stub_3rd_party_function();

    fn hello_handler (_request: &Request, response: &mut Response) -> () {
        response.send(hello_text.as_slice());
    }
    server.get("/hello", hello_handler);

    server.listen(Ipv4Addr(0,0,0,0), 6767);
}

Results in the following error:

src/test.rs:12:23: 12:33 error: can't capture dynamic environment in a fn item; use the || { ... } closure form instead
src/test.rs:12         response.send(hello_text);
                                     ^~~~~~~~~~
src/test.rs:12:23: 12:33 error: unresolved name `hello_text`.
src/test.rs:12         response.send(hello_text);
                                     ^~~~~~~~~~
error: aborting due to 2 previous errors

Now, I switch from a standard function to a closure function instead:

extern crate nickel;

use std::io::net::ip::Ipv4Addr;
use nickel::{ Nickel, Request, Response };

fn stub_3rd_party_function() -> String {
    "hello world".to_string()
}

fn main() {

    let mut server = Nickel::new();

    // assume that the variable **must** be instantiated like this
    let hello_text : String = stub_3rd_party_function();

    let hello_handler = |_request: &Request, response: &mut Response| -> () {
        response.send(hello_text.as_slice());
    };
    server.get("/hello", hello_handler);

    server.listen(Ipv4Addr(0,0,0,0), 6767);
}

Results in a different error:

src/test.rs:21:30: 21:43 error: mismatched types: expected `fn(&nickel::request::Request<'_>, &mut nickel::response::Response<'_,'_>)` but found `|&nickel::request::Request<'_>, &mut nickel::response::Response<'_,'_>|` (expected extern fn but found fn)
src/test.rs:21         server.get("/hello", hello_handler);
                                            ^~~~~~~~~~~~~
error: aborting due to previous error

Is there perhaps a way to "wrap" the closed over function with a normal one?

Since the library that I am using expects a standard function instead of a closure, I cannot use a closure. But if I do not use a closure, I cannot close over variables that are defined within the outer function, fn main () { ... }... and thus getting stuck here.

Note that above, I am using a string, hello_text, for the purposes of providing a concise code example. In this case using a static variable would suffice. However, static variables will not fix it for me, as I need to be able to assign a variable from within a fn main() the result of a function call, and then use that within my inner handler function.

like image 234
bojangle Avatar asked Jul 31 '14 22:07

bojangle


1 Answers

It says that because it’s the simple truth: a function cannot capture variables; if you put a function inside another function, that the function is inside the function rather than outside is purely a matter of namespacing and making it absolutely private and inaccessible to anything else. If you want such variable capturing, you must use a closure.

In your specific case, functions are the only way. You should consider your code to be this (I would write it this way, too, to reduce indentation if nothing else):

fn hello_handler(_request: &Request, response: &mut Response) {
    response.send(hello_text);
}

fn main() {
    let mut server = Nickel::new();
    let hello_text = "hello world";
    server.get("/hello", hello_handler);
    server.listen(Ipv4Addr(0, 0, 0, 0), 6767);
}

As you can see with this way of expressing it, hello_text is evidently inaccessible from hello_handler. There are sound technical reasons why it must be so, also—each request is handled in its own task. In this particular case, a static is the solution:

static HELLO_TEXT: &'static str = "hello world";
like image 50
Chris Morgan Avatar answered Oct 25 '22 16:10

Chris Morgan