为什么 `impl Trait` return 值实现 Send 而 `Box<dyn Trait>` 没有?
Why does an `impl Trait` return value implement Send while `Box<dyn Trait>` does not?
的解决方案建议创建一个 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对象改成Box<dyn Future<Output = i32> + Send>
,但是为什么会有这个区别?
出于人体工程学原因。 RFC 1522, conservative impl trait,专门讨论这个设计决策:
OIBITs leak through an abstract return type. 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:
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. In practice, most uses of this feature would have
to add explicit bounds for OIBITS if they wanted to be maximally usable.
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.
- In both cases, the existence of OIBIT impls is not visible without documentation tools
- 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.
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.
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. (As noted above, this is already the case for struct
definitions.)
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.
另请参阅:
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对象改成Box<dyn Future<Output = i32> + Send>
,但是为什么会有这个区别?
出于人体工程学原因。 RFC 1522, conservative impl trait,专门讨论这个设计决策:
OIBITs leak through an abstract return type. 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:
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. In practice, most uses of this feature would have to add explicit bounds for OIBITS if they wanted to be maximally usable.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.
- In both cases, the existence of OIBIT impls is not visible without documentation tools
- 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.
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.
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. (As noted above, this is already the case for
struct
definitions.)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.
另请参阅: