为什么`impl Trait`的返回值实现了`Send`,而`Box<dyn Trait>`却没有实现?

6

这里有一个解决方案来自于如何将`impl Trait`类型的变量存储在结构体中,建议创建一个`Future` trait对象。但在我的实际代码中这样做会生成一个错误,提示该类型不是 `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>,但是为什么存在这种差异呢?


好的,我已经大幅简化和概括了问题,以便关注为什么。 - Shepmaster
1个回答

2
由于人体工程学的原因。RFC 1522,保守实现特性,具体讨论了这个设计决策。
OIBITs通过一个抽象的返回类型泄漏出来。这可能被认为是有争议的,因为它有效地打开了一个通道,其中函数本地类型推断的结果影响了项目级API,但出于以下原因被认为是值得的:
- 人机工程学:Trait对象已经存在需要显式声明Send/Sync能力的问题,而不将此问题扩展到抽象返回类型是可取的。实际上,如果想要最大限度地使用此功能,大多数用途都必须添加OIBITS的显式边界。 - 实际变化很小,因为私有字段的结构体中已经存在这种情况: - 在这两种情况下,对私有实现的更改可能会改变是否实现OIBIT。 - 在这两种情况下,OIBIT impl的存在在没有文档工具的情况下是不可见的。 - 在这两种情况下,您只能通过向API或crate的测试套件添加显式trait边界来断言OIBIT impl的存在。
事实上,OIBIT的一个重要目的是穿越抽象层次并提供有关类型的信息,而不需要类型的作者明确选择。
然而,这意味着,考虑到删除OIBIT impls的方式更改了具有抽象返回类型的函数,这必须被视为一种悄无声息的破坏性变化,这可能是一个问题。(如上所述,对于结构定义已经是这种情况。)
但是,由于使用的OIBIT数量相对较小,在函数体中推断返回类型并推理此类破坏是否会发生被认为是可管理的工作量。
另请参阅:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接