简体   繁体   中英

Rust 2021 vs. 2018: trait `std::marker::Send` is not implemented - what is the correct way to do this in 2021 edition?

I am currently following a course on Rust and have found that there is a bit that compiles with the 2018 edition of Rust but not the 2021 edition. While I could just change my edition and continue on, I'd like to understand further what would be correct way to proceed if I were using the 2021 edition and why the difference occurs?

The error that comes with the 2021 edition is:

let handle = std::thread::spawn(move || {
             ^^^^^^^^^^^^^^^^^^
             *mut [T] cannot be sent between threads safely
    threaded_fun(&mut *raw_s.0)
});
=help: ... the trait `Send` is not implemented for `*mut [T]`

The original code was working on multithreaded sort function, but I've tried to take out as much as I think I could so that it's just splitting out a vector and printing it's progress.

use std::fmt::Debug;

struct RawSend<T>(*mut [T]); // one element tuple

unsafe impl<T> Send for RawSend<T> {}

pub fn threaded_fun<T: 'static + PartialOrd + Debug + Send>(v: &mut [T]) {
    if v.len() <= 1 {
        return;
    }
    let p = v.len()/2;
    println!("{:?}", v);

    let (a, b) = v.split_at_mut(p);

    let raw_a: *mut [T] = a as *mut [T];
    let raw_s = RawSend(raw_a);

    unsafe {
        let handle = std::thread::spawn(move || {
            threaded_fun(&mut *raw_s.0)
        });
        threaded_fun(&mut b[..]);

        // unsafe is required because compiler doesn't know we combine these
        handle.join().ok();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn threaded_test() {
        let mut v = vec![1,2,3,4,5,6,7,8,9,10];
        threaded_fun(&mut v);
        panic!(); // Test is just to activate the function. `cargo test`
    }
}

This is because Edition 2021 has partial captures in closures .

What this means is that in Rust 2018,

|| a.b

will capture "a" in its entirety, but in Rust 2021, it will only perform a partial capture of ab , and if the structure has other fields those remain available to the creator of the closure.

In your case however, it means raw_s.0 is what's captured and used for the computation of the closure's traits, and since it's not Send (as it's a raw pointer) the closure is not Send anymore either. The change in traits computation was specifically noted in the editions changelog .

You can fix that by forcing a capture of the structure itself eg

let raw_s = raw_s;

or

let _ = &raw_s;

Note that cargo has a cargo fix subcommand which can migrate this sort of changes for you (using the --edition flag).

This is because edition 2021 changed how closures capture. In 2018, they capture entire structs, but in 2021, they will capture individual struct members.

This causes the closure to attempt to capture the raw pointer ( raw_s.0 ) instead of the whole RawSend . You can work around this by explicitly forcing raw_s to move:

    unsafe {
        let handle = std::thread::spawn(move || {
            let raw_s = raw_s;
            threaded_fun(&mut *raw_s.0)
        });
        threaded_fun(&mut b[..]);

        // unsafe is required because compiler doesn't know we combine these
        handle.join().ok();
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM