复制 std::thread::spawn 和 Send 行为,但具有不同的特征和功能

Copying std::thread::spawn and Send behavior, but with a different trait and function

我正在学习 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
    });
}

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

A closure is Send if all variables captured by non-unique immutable reference are Sync, and all values captured by unique immutable or mutable reference, copy, or move are Send.

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

因此,由于闭包类型是完全不透明的,因此您现在无法真正实现它

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

This trait is automatically implemented when the compiler determines it’s appropriate.

plans to move the implementation fully into the standard library (tracking issue),这应该使得具有相似的用户定义标记特征成为可能。有可能当此功能稳定后您可以编写:

auto trait SomeTrait {}

struct StructWithTrait {}

struct StructWithoutTrait {}
impl !SomeTrait for StructWithoutTrait {}

并获得类似于 Send 的行为。

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

  1. 如果有明确的impl AutoTrait for T,那么T总是impls AutoTrait(注意因为Send is an unsafe trait你需要unsafe impl Send for T,但是总的原则保持不变)。
  2. 否则,如果有负面影响 impl !AutoTrait for T,则T不会实现AutoTrait。请注意,对于同一类型的同一特征同时具有正面和负面影响是错误的。
  3. 否则,如果T的任何字段没有实现AutoTraitT也不会实现它。例如,如果T定义为struct T(U);,并且有impl !AutoTrait for U,那么T并没有实现AutoTrait
  4. 否则,T 实施 AutoTrait

auto traits 和 negative impls 都是高度不稳定的特性,目前不打算在标准库之外使用。如果你真的想要,你可以,尽管因为编译器必须能够自动实现特征,它不能包含任何关联项(方法、关联类型、关联常量)。如果您删除 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.