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`
}
}
CodePudding user response:
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 a.b
, 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 e.g.
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).
CodePudding user response:
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();
}