[英]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? ). 这由Fn
, FnMut
和FnOnce
特征表示(在问题的答案中解释了何时关闭实现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.