Rust 2021 vs 2018:Send trait未实现问题的正确解决方式
问题说明
学习Rust过程中发现某段代码在Rust 2018 edition下可正常编译,在2021 edition下编译失败。当然可以直接切换版本继续学习,但需要明确2021 edition下的正确实现方式,以及两个版本出现编译差异的原因。
2021 edition 报错信息
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]`
最小复现代码
原始代码用于实现多线程排序功能,已精简无关逻辑,仅保留拆分向量、打印运行进度的核心逻辑,完整示例如下:
use std::fmt::Debug; struct RawSend<T>(*mut [T]); // 单元素元组 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块 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!(); // 测试仅用于触发函数执行,执行`cargo test`即可运行 } }
版本差异原因
两个版本编译结果不同,核心是Rust 2021 edition引入了闭包精确捕获规则:
- 2018 edition中,闭包会捕获整个
raw_s变量,由于已经为RawSend<T>手动实现了Sendtrait,闭包整体满足跨线程传递的Send约束,因此编译通过。 - 2021 edition中,闭包只会捕获自身实际用到的字段,上述闭包仅用到了
raw_s.0,该字段类型为*mut [T],而Rust中裸指针默认不实现Sendtrait,编译器不会再套用外层RawSend的Send实现,因此抛出编译错误。
2021 edition 修复方案
只需要在闭包内显式绑定整个raw_s变量,强制闭包捕获完整的RawSend实例而非单独的内部裸指针字段即可,修改std::thread::spawn对应的闭包代码:
let handle = std::thread::spawn(move || { let raw_s = raw_s; // 显式绑定,强制捕获整个RawSend实例 threaded_fun(&mut *raw_s.0) });
该修改无任何运行时开销,仅调整编译器的闭包捕获判定逻辑,完全符合原有unsafe代码的安全约定。
内容的提问来源于stack exchange,提问作者DoubleDouble
相关产品推荐
相关产品推荐

