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

Afs*_*ani 4 multithreading closures rust

它是否创建一个新线程然后在新线程中执行该匿名函数?

当我使用闭包时,我注意到许多所有权/借用限制.例如,如果我有Fn(),我不能在闭包内传递一个可变变量,或者它需要用一个包裹Mutex:

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

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

如果一个闭包可能不安全,那么场景后面会发生异步或并行的事情,这就是为什么Rust编译器不允许我编译这样的代码.

我可能会感到困惑,因为我来自JavaScript/Python世界,那里的情况完全不同.

DK.*_*DK. 12

这个问题分为两层.

首先,Rust中的闭包只是一个匿名定义的类型,它实现了一个或多个"可调用"特征.例如,这个:

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

脱糖成类似于:

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,));
}
Run Code Online (Sandbox Code Playgroud)

注意:上面的代码依赖于不稳定的内部细节,不适用于稳定的编译器.它仅供参考; 你应该自己使用这种模式.

没有线程涉及,没有任何神奇的事情发生.它们归结为常规函数调用,带有额外的初始"上下文"参数.

这将我们带到第二层,这就是为什么你的特定代码不起作用的原因:因为你告诉编译器禁止它.关于callable的一个关键问题是如何将上下文传递给callable的代码.这是由代表Fn,FnMut以及FnOnce特性(这是在回答解释这个问题什么时候闭合实现FN,FnMut和FnOnce? ).通过采取&Fn(f64),你限制自己只接受需要不可变访问其上下文的闭包.

如果您希望闭包能够改变其上下文,则需要使用FnMut.或者,如果你只需要调用一次闭包,你可以使用FnOnce(虽然不像你在你的例子中那样做的特征对象).

  • 哦,哇,这是一个很好且清晰的解释。非常感谢DK。 (2认同)