Rust与区块链编程

Rust thread 的closure若没有 move 报错逻

2020-06-07  本文已影响0人  红叔笔记

引子: thread构建子进程, 并利用channel的一般方式如下:

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn( move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

接下来是一个没事找事的操作, 如果把 move 关键字去掉, 会发生什么事呢? 编译器报错如下:

error[E0277]: `std::sync::mpsc::Sender<std::string::String>` cannot be shared between threads safely
   --> src/main.rs:7:5
    |
7   |     thread::spawn( || {
    |     ^^^^^^^^^^^^^ `std::sync::mpsc::Sender<std::string::String>` cannot be shared between threads safely
    |
    = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Sender<std::string::String>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Sender<std::string::String>`
    = note: required because it appears within the type `[closure@src/main.rs:7:20: 10:6 tx:&std::sync::mpsc::Sender<std::string::String>]`

本文就是想知道, 为什么去掉move会报这个编译错误。

场景1 分析: 没有move的情况

thread::spawn( || {
    let val = String::from("hi");
    tx.send(val).unwrap();
});

这里的thread::spawn 里的closure没有加 move

说明这里的tx是reference, 而不是有ownership的type。

又因为Sender实现了send, 结合现在tx的类别, 所以可以推断: &std::sync::mpsc::Sender<std::string::String> 也实现了 Send (这里前面是 ‘&’ 是因为 tx 没有所有权, 即上面说的reference)

既然 &std::sync::mpsc::Sender<std::string::String> 实现了 Send, 那么可以继续推断, std::sync::mpsc::Sender<std::string::String> 应该实现了 Sync 【这里的依据是 if &T is Send, then T is Sync】

现在冲突来了, 上面的检查逻辑需要Sender是实现了Sync, 而实际Send里是明确禁止了Sync, 也即不支持:(源码如下:)

#[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> {}

场景2 分析: 有move的情况

thread::spawn( move|| {
    let val = String::from("hi");
    tx.send(val).unwrap();
});

这里有了move, 表示tx的所有权move到子线程

因为Sender实现了send, 结合tx的类别(独立所有权), 可以推断: std::sync::mpsc::Sender<std::string::String> 也实现了 Send

到这一步, 发现Sender没有对Sync有任何要求, 所以没有触发冲突。

这是我目前感觉从编译器的角度逻辑最简单的解释, 感觉编译器的逻辑是简单的数学逻辑, 有明确的原因和结果, 但是我听大家来分析的话, 感觉就像听天书。

下一步action: 以后出错, 多从源码和编译器的角度看逻辑, 也许会柳暗花明?

当然, 上面的具体分析是否正确我也不确定, 欢迎大家反馈。

上一篇下一篇

猜你喜欢

热点阅读