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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.