看这段代码:
fn main() {
let counter = Rc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Rc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
编译器报错如下:
error[E0277]: `std::rc::Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely
--> src/main.rs:11:22
|
11 | let handle = thread::spawn(move || {
| ______________________^^^^^^^^^^^^^_-
| | |
| | `std::rc::Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely
12 | | let mut num = counter.lock().unwrap();
13 | |
14 | | *num += 1;
15 | | });
| |_________- within this `[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`
|
= help: within `[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::sync::Mutex<i32>>`
= note: required because it appears within the type `[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`
有人说, 很简单, 因为Arc就是为了解决Rc的问题诞生的, 这个逻辑简直是毫无逻辑, 很简单, 编译器为什么报错呢? 编译器比人简单多了, 而且它的逻辑还很容易理解。
我的理解:
首先看下spawn的签名:
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
Builder::new().spawn(f).expect("failed to spawn thread")
}
这里很明确的要求是, spwan的参数是一个closure, 并且该closure捕获的变量要求实现Send。
我认为编译器报错的逻辑核心就是在这里, Rc里没有实现Send, 而Arc实现了逻辑。
看下Arc的源码:
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
所以, Arc符合签名要求, 而Rc不符合要求。
结合前面的channel的例子, 因为Sender实现了Send, 源码如下:
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: Send> Send for Sender<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> !Sync for Sender<T> {}
小结: 目前看 spawn的方法签名默认就要求实现Send, 没有实现Send的自然就报错, Rc的报错原因即是如此。
所以, 说啥Arc是为了解决Rc的问题, 本质还是Arc实现了Send。
当然, 上面的closure 里的签名其实我还是不太理解的一个地方就是:
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
这里对F的要求是实现Send, 我一直理解是某个变量类型实现了Send, 但是一个closure实现Send这个trait是什么意思? 目前只能勉强理解为捕获的变量实现Send 这个trait。
欢迎反馈。
更新: 来自群友北纬27度的解释:
可以这么理解,因为closure本质上是一个匿名结构体,一个结构体要实现Send的前提是他的所有成员都实现了Send,而这个匿名结构体的成员就是他捕获的变量,所以那些变量也要实现Send才行
网友评论