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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With