How do Rust closures work and how does it execute a closure?

无人久伴 提交于 2019-12-10 18:16:20

问题


Does it create a new thread and then execute that anonymous function inside the new thread?

I noticed many ownership / borrowing restrictions when I'm working with a closure. For example, if I have Fn(), I cannot pass a mutable variable inside the closure or it needs to be wrapped with a Mutex:

fn helloworld(f: &Fn(f64)) {
    f(42f64);
}

pub fn main() {
    let mut killer = 2;
    helloworld(&|n| {
        println!("{}", n);
        killer += 1;
    });
}

If a closure can be unsafe like that then something asynchronous or parallel is going on behind the scene and that's why Rust compiler doesn't let me to compile such code.

I might just be confused because I'm coming from a JavaScript / Python world and things are completely different there.


回答1:


There are two layers to this question.

First, a closure in Rust is just an anonymously-defined type that implements one or more "callable" traits. For example, this:

fn main() {
    let a = 6;
    let closure = |b| {
        println!("product is: {}", a * b);
    };
    closure(7);
}

is de-sugared into something similar to:

fn main() {
    let a = 6;
    let closure = {
        struct Closure<'a> {
            a: &'a i32,
        }
        impl<'a> Fn<(i32,)> for Closure<'a> {
            extern "rust-call" fn call(&self, (b,): (i32,)) {
                println!("product is: {}", (*self.a) * b);
            }
        }
        impl<'a> FnMut<(i32,)> for Closure<'a> {
            extern "rust-call" fn call_mut(&mut self, args: (i32,)) {
                self.call(args)
            }
        }
        impl<'a> FnOnce<(i32,)> for Closure<'a> {
            type Output = ();
            extern "rust-call" fn call_once(self, args: (i32,)) {
                self.call(args)
            }
        }
        Closure {
            a: &a,
        }
    };
    FnOnce::call_once(closure, (7,));
}

Note: the above code relies on unstable, internal details and will not work on a stable compiler. It is provided for explanation only; you should not use this pattern yourself.

There's no threading involved, and nothing magical is happening. They boil down to a regular function call with an extra initial "context" argument.

This brings us to the second layer, which is why your specific code doesn't work: because you told the compiler to forbid it. One critical concern for callables is how the context is passed to the callable's code. This is represented by the Fn, FnMut and FnOnce traits (which are explained in the answer to the question When does a closure implement Fn, FnMut and FnOnce?). By taking &Fn(f64), you've restricted yourself to only accepting closures which require immutable access to their context.

If you want a closure to be able to mutate its context, you need to use FnMut instead. Or, if you only need to call a closure once, you can use FnOnce (although not as a trait object like you're doing in your example).



来源:https://stackoverflow.com/questions/45935100/how-do-rust-closures-work-and-how-does-it-execute-a-closure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!