为什么"impl Trait"返回值实现发送,而"Box<dyn Trait>"没有?



解决方案来自如何在结构中存储类型为"impl Trait"的变量?建议创建一个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>>`

从 Rust 中的线程之间发送 trait 对象,我已经知道我可以将 trait 对象更改为Box<dyn Future<Output = i32> + Send>,但为什么存在这种差异?

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

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

  • 人体工程学:特质对象已经存在明确需要的问题 声明Send/Sync能力,而不是将此问题扩展到抽象 返回类型是可取的。实际上,此功能的大多数用途都会具有 为 OIBIT 添加显式边界(如果它们希望最大程度地可用(。

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

    • 在这两种情况下,对专用实现的更改都可能会更改 OIBIT 是否为 实施与否。
    • 在这两种情况下,如果没有文档工具,OIBIT impls 的存在是不可见
    • 在这两种情况下,您只能断言 OIBIT impls 的存在 通过将显式特征边界添加到 API 或 crate 的测试套件中。

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

但是,这意味着必须将其视为对 以删除 OIBIT impls 的方式更改具有抽象返回类型的函数, 这可能是一个问题。(如上所述,struct定义。

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

另请参阅:

  • 什么是 Rust 中的自动特性?

最新更新