2021 vs 2018 edition: cannot be sent between threads safely

My code bellow compiles and works just fine with Rust edition 2018:

fn rust_f(_: impl FnOnce() + Send + 'static) {
    todo!()
}

unsafe impl Send for CFnOnce {}

#[repr(C)]
pub struct CFnOnce {
    cb: extern "C" fn(*mut ::std::os::raw::c_void),
    ctx: *mut ::std::os::raw::c_void,
}

fn c_f(c_cb: CFnOnce) {
    let mut r_cb = move || {
        (c_cb.cb)(c_cb.ctx);
    };
    rust_f(r_cb)
}

But with edition 2021 I got error:

error[E0277]: `*mut c_void` cannot be sent between threads safely
  --> src/main.rs:21:5
   |
18 |       let mut r_cb = move || {
   |  ____________________-
19 | |         (c_cb.cb)(c_cb.ctx);
20 | |     };
   | |_____- within this `[closure@src/main.rs:18:20: 20:6]`
21 |       rust_f(r_cb)
   |       ^^^^^^ `*mut c_void` cannot be sent between threads safely

And I can not undernstand why I got this error and how to fix.

2021 can capture only several fields of struct, and if I use only c_cb.ctx inside closure,
that error message has sense, but I obviously use both fields of struct inside closure.
And for example such trick:

fn c_f(c_cb: CFnOnce) {
    let mut r_cb = move || {
        let _ = c_cb;//<-- TRICK
        (c_cb.cb)(c_cb.ctx);
    };
    rust_f(r_cb)
}

changes nothing,
so any idea what is going on, is this bug of rustc ?

Try let c_cb = c_cb;. let _ = c_cb; doesn't use any of c_cb's fields and thus doesn't cause anything to get captured in the 2021 edition.

1 Like

The automatic migration does insert a trick almost like the thing you tried, but it's actually let _ = &c_cb;.

1 Like

Yes, I know this trick, but I thought that this cause "borrow", and in my case I need "capture" instead of borrow, but in fact all works just fine. It compiles without any problem.