[英]Why does an `impl Trait` return value implement Send while `Box<dyn Trait>` does not?
The solution from How do I store a variable of type `impl Trait` in a struct? 我如何在结构中存储`impl Trait`类型变量的解决方案? suggests creating a
Future
trait object. 建议创建一个
Future
trait对象。 Doing that in my real code generates an error that the type is not Send
, but the only difference between the working and non working version is the presence or absence of the cast to dyn Future
. 在我的真实代码中这样做会产生一个错误,即类型不是
Send
,但是工作版本和非工作版本之间的唯一区别是是否存在dyn Future
为dyn Future
。
Why does the compiler see these as different and how do I resolve the problem? 为什么编译器将它们视为不同的,我该如何解决该问题?
Here's a simplified version of the problem: 这是问题的简化版本:
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>>`
From Sending trait objects between threads in Rust , I already know that I can change the trait object to Box<dyn Future<Output = i32> + Send>
, but why does this difference exist? 通过在Rust中的线程之间发送特征对象 ,我已经知道可以将特征对象更改为
Box<dyn Future<Output = i32> + Send>
,但是为什么存在这种区别?
For ergonomic reasons. 出于人体工程学的原因。 RFC 1522, conservative impl trait , specifically discusses this design decision:
RFC 1522,保守的隐含特性 ,专门讨论了此设计决策:
OIBITs leak through an abstract return type.
OIBIT通过抽象返回类型泄漏。 This might be considered controversial, since it effectively opens a channel where the result of function-local type inference affects item-level API, but has been deemed worth it for the following reasons:
这可能会引起争议,因为它有效地打开了一个通道,在该通道中,函数局部类型推断的结果会影响项目级API,但由于以下原因,它被认为是值得的:
Ergonomics: Trait objects already have the issue of explicitly needing to declare
Send
/Sync
-ability, and not extending this problem to abstract return types is desirable.人机工程学:特性对象已经存在明确需要声明“
Send
/Sync
问题,并且不希望将此问题扩展到抽象返回类型。 In practice, most uses of this feature would have to add explicit bounds for OIBITS if they wanted to be maximally usable.在实践中,如果希望最大程度地使用此功能,则大多数使用都必须为OIBITS添加显式边界。
Low real change, since the situation already somewhat exists on structs with private fields:
实际变化不大,因为这种情况在带有私有字段的结构上已经有些存在:
- In both cases, a change to the private implementation might change whether a OIBIT is implemented or not.
在这两种情况下,对私有实施的更改都可能会更改是否实施OIBIT。
- In both cases, the existence of OIBIT impls is not visible without documentation tools
在这两种情况下,如果没有文档工具,OIBIT impls的存在是不可见的
- In both cases, you can only assert the existence of OIBIT impls by adding explicit trait bounds either to the API or to the crate's test suite.
在这两种情况下,您只能通过在API或板条箱的测试套件中添加显式特征范围来断言OIBIT impls的存在。
In fact, a large part of the point of OIBITs in the first place was to cut across abstraction barriers and provide information about a type without the type's author having to explicitly opt in.
实际上,首先,OIBIT的主要目的是跨越抽象障碍并提供有关类型的信息,而类型的作者不必明确选择加入。
This means, however, that it has to be considered a silent breaking change to change a function with an abstract return type in a way that removes OIBIT impls, which might be a problem.
但是,这意味着必须以消除OIBIT impls的方式来更改具有抽象返回类型的函数,才能将其视为沉默的突破性更改,这可能是一个问题。 (As noted above, this is already the case for
struct
definitions.)(如上所述,
struct
定义已经是这种情况。)But since the number of used OIBITs is relatively small, deducing the return type in a function body and reasoning about whether such a breakage will occur has been deemed as a manageable amount of work.
但是,由于使用的OIBIT的数量相对较少,因此在函数体中推断出返回类型并推理是否会发生这种中断已被认为是可管理的工作量。
See also: 也可以看看:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.