`不?,rust,traits"/>

首页 > 解决方案 > 为什么`impl Trait`返回值在`Box中实现Send`不?

问题描述

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,我已经知道我可以将 trait object 更改为Box<dyn Future<Output = i32> + Send>,但是为什么会存在这种差异呢?

标签: rusttraits

解决方案


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

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

  • 人机工程学:特征对象已经存在显式需要声明Send/能力的问题,Sync并且不将此问题扩展到抽象返回类型是可取的。在实践中,如果想要最大程度地使用此功能,则必须为 OIBITS 添加明确的界限。

  • 实际变化很小,因为这种情况已经存在于具有私有字段的结构上:

    • 在这两种情况下,对私有实现的更改可能会改变是否实现了 OIBIT。
    • 在这两种情况下,如果没有文档工具,OIBIT impls 的存在是不可见的
    • 在这两种情况下,您只能通过向 API 或 crate 的测试套件添加显式特征边界来断言 OIBIT impls 的存在。

事实上,OIBIT 的很大一部分首先是跨越抽象障碍并提供有关类型的信息,而无需类型的作者明确选择加入。

然而,这意味着必须将其视为一种无声的破坏性更改,以通过删除 OIBIT impls 的方式更改具有抽象返回类型的函数,这可能是一个问题。(如上所述,这已经是struct 定义的情况。)

但由于使用的 OIBIT 的数量相对较少,因此在函数体中推导返回类型并推理是否会发生这种损坏已被认为是可管理的工作量。

也可以看看:


推荐阅读