美文网首页Rust与区块链编程
Rust thread 的closure若没有 move 报错逻

Rust thread 的closure若没有 move 报错逻

作者: 红叔笔记 | 来源:发表于2020-06-07 15:15 被阅读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: 以后出错, 多从源码和编译器的角度看逻辑, 也许会柳暗花明?

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

    相关文章

      网友评论

        本文标题:Rust thread 的closure若没有 move 报错逻

        本文链接:https://www.haomeiwen.com/subject/vkectktx.html