How to send function closure to another thread?

The error is "the trait std::marker::Sync is not implemented for dyn std::ops::Fn() -> i32" but I really need that

use std::{sync::Arc, thread};

type CB<'a> = &'a dyn Fn() -> i32;

fn main() {
    let x = 4;
    let f: Arc<CB> = Arc::new(&|| {
        println!("ok {}", x);
        3
    });

    thread::spawn(move || {
        let f = Arc::clone(&f);
        f();
    });
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0277]: `dyn std::ops::Fn() -> i32` cannot be shared between threads safely
   --> src/main.rs:12:5
    |
12  |     thread::spawn(move || {
    |     ^^^^^^^^^^^^^ `dyn std::ops::Fn() -> i32` cannot be shared between threads safely
    |
    = help: the trait `std::marker::Sync` is not implemented for `dyn std::ops::Fn() -> i32`
    = note: required because of the requirements on the impl of `std::marker::Send` for `&dyn std::ops::Fn() -> i32`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<&dyn std::ops::Fn() -> i32>`
    = note: required because it appears within the type `[closure@src/main.rs:12:19: 15:6 f:std::sync::Arc<&dyn std::ops::Fn() -> i32>]`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

You are using trait objects (i.e. the dyn Trait syntax), which forget everything about the contained type not explicitly specified. So if you want to send it to another thread, you must explicitly tell it not to forget that it may be sent to another thread. Additionally, don't put closures behind references.

So to make it compile, you need the type Arc<dyn Fn() -> i32 + Send + Sync>. playground

However you can also just not convert your closure to a trait object, in which case the compiler doesn't forget that your closure was thread-safe. That looks like this:

fn main() {
    let x = 4;
    let f = Arc::new(move || {
        println!("ok {}", x);
        3
    });

    thread::spawn(move || {
        let f = Arc::clone(&f);
        f();
    });
}

playground

Finally in your case you are giving away ownership anyway, so you don't need an arc.

use std::thread;

fn main() {
    let x = 4;
    let f = move || {
        println!("ok {}", x);
        3
    };

    thread::spawn(move || {
        f();
    });
}

playground

3 Likes

Yeah, but my problem needs to store an array of closures, so I must use reference not just closure

dyn std::ops::Fn()` cannot be shared between threads safely

use std::thread;

fn main() {
    let functions: [&dyn Fn(); 2] = [&|| {
        println!("One closure");
    }, &|| {
        println!("Second");
    }];

    thread::spawn(move || {
        for f in &functions {
            f();
        }
    }).join().unwrap();
}

No, you should not use references because a reference is fundamentally not an owned type, and you want ownership. Use e.g. a Box instead.

let functions: [Box<dyn Fn() + Send>; 2] = [Box::new(|| {
    println!("One closure");
}), Box::new(|| {
    println!("Second");
})];
2 Likes

Great, thanks!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.