简体   繁体   English

Rust闭包如何工作以及如何执行闭包?

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

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()我不能在闭包内传递一个可变变量,或者它需要用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. 如果一个闭包可能不安全,那么场景后面会发生异步或并行的事情,这就是为什么Rust编译器不允许我编译这样的代码。

I might just be confused because I'm coming from a JavaScript / Python world and things are completely different there. 我可能会感到困惑,因为我来自JavaScript / Python世界,那里的情况完全不同。

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. 首先,Rust中的闭包只是一个匿名定义的类型,它实现了一个或多个“可调用”特征。 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. 关于callable的一个关键问题是如何将上下文传递给callable的代码。 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? ). 这由FnFnMutFnOnce特征表示(在问题的答案中解释了何时关闭实现Fn,FnMut和FnOnce? )。 By taking &Fn(f64) , you've restricted yourself to only accepting closures which require immutable access to their context. 通过&Fn(f64) ,你只限于接受需要对其上下文进行不可变访问的闭包。

If you want a closure to be able to mutate its context, you need to use FnMut instead. 如果您希望闭包能够改变其上下文,则需要使用FnMut 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). 或者,如果你只需要调用一次闭包,你可以使用FnOnce (虽然不像你在你的例子中那样做一个特征对象)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM