为什么“ impl Trait”返回值实现了“ Box <dyn Trait>”没有发送的情况?

时间:2019-11-25 17:34:33

标签: rust traits

How do I store a variable of type `impl Trait` in a struct?中的解决方案建议创建一个Future特征对象。在我的真实代码中这样做会产生一个错误,指出类型不是Send,但是有效版本和无效版本之间的唯一区别是是否存在强制转换为dyn Future

为什么编译器认为这些不同,我该如何解决该问题?

这是问题的简化版本:

use std::future::Future;

fn uses_impl_trait() -> impl Future<Output = i32> {
    async { 42 }
}

fn uses_trait_object() -> Box<dyn Future<Output = i32>> {
    Box::new(async { 42 })
}

fn requires_send<T: Send>(_: T) {}

fn example() {
    requires_send(uses_impl_trait()); // Works
    requires_send(uses_trait_object()); // Fails
}
error[E0277]: `dyn std::future::Future<Output = i32>` cannot be sent between threads safely
  --> src/lib.rs:15:19
   |
11 | fn requires_send<T: Send>(_: T) {}
   |    -------------    ---- required by this bound in `requires_send`
...
15 |     requires_send(uses_trait_object());
   |                   ^^^^^^^^^^^^^^^^^^^ `dyn std::future::Future<Output = i32>` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `dyn std::future::Future<Output = i32>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<dyn std::future::Future<Output = i32>>`
   = note: required because it appears within the type `std::boxed::Box<dyn std::future::Future<Output = i32>>`

Sending trait objects between threads in Rust开始,我已经知道可以将特征对象更改为Box<dyn Future<Output = i32> + Send>,但是为什么存在这种区别?

1 个答案:

答案 0 :(得分:2)

出于人体工程学的原因。 RFC 1522, conservative impl trait,专门讨论了此设计决策:

  

OIBIT通过抽象返回类型泄漏。这可能会引起争议,因为   它有效地打开了一个通道,函数本地类型推断的结果会影响该通道   项目级API,但由于以下原因而被认为值得:

     
      
  • 人体工程学:特质对象已经存在明确需要   声明Send / Sync-功能,并且不将此问题扩展为抽象   返回类型是可取的。实际上,此功能的大多数用途是   如果希望最大程度地使用OIBITS,则为它们添加明确的界限。

  •   
  • 变化不大,因为带有私有字段的结构已经有点存在这种情况了

         
        
    • 在两种情况下,对私有实施方式的更改都可能会更改OIBIT是否为   实施与否。
    •   
    • 在两种情况下,如果没有文档工具,OIBIT impls的存在是不可见的
    •   
    • 在两种情况下,您只能断言OIBIT impls的存在   通过向API或板条箱的测试套件添加显式特征范围。
    •   
  •   
     

实际上,OIBIT的主要目的是削减   跨越抽象障碍,并提供有关类型的信息,而无需   类型的作者必须明确选择加入。

     

但是,这意味着它必须被视为对   更改具有抽象返回类型的函数,以消除OIBIT隐含的方式,   这可能是个问题。 (如上所述,struct就是这种情况   定义。)

     

但是由于使用的OIBIT的数量相对较少,因此推导了返回类型   在功能主体中以及是否会发生这种破损的推理有   被认为是可管理的工作量。

另请参阅: