症状
错误同时点名“走私者”与“关卡”:error[E0277]: Rc<String> cannot be sent between threads safely,note 说明它出现在闭包内、是 std::thread::spawn 的要求。Rc 使用非原子引用计数,跨线程发送会让计数器竞态,编译期直接拒绝 spawn 而不是放行数据竞争。单线程代码里 Rc 随便编译,失败恰在闭包跨越 spawn 时出现——这也是“重构到线程池后错误才来”的原因。note 里的类型链就是审计清单。
诊断思路
先读 E0277 完整诊断,非 Send 类型与捕获它的闭包都被点名;再 grep 所有 Rc::new、Rc<、RefCell,每处被线程闭包捕获的都是迁移面;最后确认替换后编译与测试双绿。裸指针、非线程安全的 FFI 包装、全局惰性初始化捕获进闭包,是三个易漏变体。
根因
std::thread::spawn 要求闭包及其捕获物满足 Send(闭包按值跨越线程边界)。Rc 的非原子计数在跨线程下会竞态,因此编译器在编译期拒绝,这正是 Rust 把数据竞争消灭在编译期的机制在工作。
修复与验证
按共享风格替换类型:跨线程可变共享用 Arc<Mutex
代码示例
error[E0277]: `Rc<String>` cannot be sent between threads safely
--> src/main.rs:12:19
|
12 | thread::spawn(move || {
| ------------- ^-- within the closure, `Rc<String>`
| | cannot be sent between threads safely
= note: required because it appears within the closure
= note: required by `std::thread::spawn`
use std::sync::{Arc, Mutex};
// shared mutable state across threads:
let counter = Arc::new(Mutex::new(0usize));
let handles: Vec<_> = (0..4).map(|_| {
let counter = Arc::clone(&counter);
thread::spawn(move || {
*counter.lock().unwrap() += 1; // Send + Sync satisfied
})
}).collect();
for h in handles { h.join().unwrap(); }
let config = Arc::new(load_config()); // no Mutex: immutable share
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send(expensive(chunk)).unwrap(); // ownership moved in, result out
});