【问题标题】:Copying std::thread::spawn and Send behavior, but with a different trait and function复制 std::thread::spawn 和 Send 行为,但具有不同的特征和功能
【发布时间】:2022-01-06 11:09:08
【问题描述】:

我正在学习 Rust 并且遇到了以下问题,这对我来说并不明显。 我在库中看到了一个 std::thread::spawn,查看了实现,发现某些类型需要实现 Send 特征才能将某些内容发送到另一个线程。 我试图用我自己的函数和我自己的特征来复制行为,但是编译器成功地编译了代码,而没有抱怨我的类型没有实现特征。 我错过了什么?

pub trait SomeTrait {
    fn bar(&self);
}

#[derive(Debug)]
struct StructWithoutTrait {
    _data: Box<i32>
}

#[derive(Debug)]
struct StructWithTrait {
    _data: Box<i32>
}

impl SomeTrait for StructWithTrait {
    fn bar(&self) {}
}

fn foo<F, T>(f: F) -> T
where
    F: FnOnce() -> T,
    F: SomeTrait + 'static,
    T: SomeTrait + 'static
{
    f.bar();
    f()
}

impl<F, T> SomeTrait for F
where 
    F: FnOnce() -> T,
    T: SomeTrait
{
    fn bar(&self) {}
}

fn main() {
    let without_trait = StructWithoutTrait { _data: Box::new(1) };
    let with_trait = StructWithTrait { _data: Box::new(2) };
    let x = std::rc::Rc::new(1);

    foo(move || {
        println!("{:?}", without_trait);
        println!("{:?}", x);
        with_trait
    });
}

【问题讨论】:

    标签: rust closures traits


    【解决方案1】:

    Sendan auto trait。自动特征有点像编译器实现的;确定类型T 是否实现自动特征AutoTrait 的步骤如下:

    1. 如果存在显式impl AutoTrait for T,则T 总是隐含AutoTrait(请注意,因为Send is an unsafe trait 你需要unsafe impl Send for T,但一般原则保持不变。
    2. 否则,如果有一个否定的实现impl !AutoTrait for T,那么T 不会实现AutoTrait。请注意,对于同一类型,具有相同特征的正面和负面 impl 是错误的。
    3. 否则,如果T的任何字段没有实现AutoTraitT也不会实现。比如T定义为struct T(U);,有impl !AutoTrait for U,那么T就没有实现AutoTrait
    4. 否则,T 实现 AutoTrait

    自动特征和否定 impl 都是高度不稳定的特性,目前不打算在标准库之外使用。如果你真的想要,你可以,尽管因为编译器必须能够自动实现 trait,它不能包含任何关联项(方法、关联类型、关联 const)。如果您删除 bar() 方法,您的代码将如下所示:

    #![feature(auto_traits, negative_impls)]
    
    pub auto trait SomeTrait {}
    
    #[derive(Debug)]
    struct StructWithoutTrait {
        _data: Box<i32>
    }
    
    impl !SomeTrait for StructWithoutTrait {}
    
    #[derive(Debug)]
    struct StructWithTrait {
        _data: Box<i32>
    }
    
    fn foo<F, T>(f: F) -> T
    where
        F: FnOnce() -> T,
        F: SomeTrait + 'static,
        T: SomeTrait + 'static
    {
        f()
    }
    fn main() {
        let without_trait = StructWithoutTrait { _data: Box::new(1) };
        let with_trait = StructWithTrait { _data: Box::new(2) };
        let x = std::rc::Rc::new(1);
    
        foo(move || {
            println!("{:?}", without_trait);
            println!("{:?}", x);
            with_trait
        });
    }
    

    Playground.

    【讨论】:

      【解决方案2】:

      我很确定这是编译器行为:

      如果由非唯一不可变引用捕获的所有变量都是 Sync,并且由唯一不可变或可变引用、复制或移动捕获的所有值都是 Send,则闭包是 Send。

      ~https://doc.rust-lang.org/reference/types/closure.html

      因此,您现在无法真正实现闭包类型完全不透明的方式

      【讨论】:

        【解决方案3】:

        目前Send 由编译器专门处理。引用the documentation:

        当编译器确定它合适时,这个特性会自动实现。

        plans 将实现完全移入标准库 (tracking issue),这应该可以让用户定义类似的标记特征。当此功能稳定后,您可能可以编写:

        auto trait SomeTrait {}
        
        struct StructWithTrait {}
        
        struct StructWithoutTrait {}
        impl !SomeTrait for StructWithoutTrait {}
        

        并获得类似于Send 的行为。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-10-07
          • 2015-03-09
          • 2022-01-11
          • 1970-01-01
          • 2016-02-13
          • 1970-01-01
          • 2020-12-21
          • 1970-01-01
          相关资源
          最近更新 更多