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开始,我已经知道可以将特征对象更改为Box<dyn Future<Output = i32> + Send>
,但是为什么存在这种区别?
答案 0 :(得分:2)
出于人体工程学的原因。 RFC 1522, conservative impl trait,专门讨论了此设计决策:
OIBIT通过抽象返回类型泄漏。这可能会引起争议,因为 它有效地打开了一个通道,函数本地类型推断的结果会影响该通道 项目级API,但由于以下原因而被认为值得:
人体工程学:特质对象已经存在明确需要 声明
Send
/Sync
-功能,并且不将此问题扩展为抽象 返回类型是可取的。实际上,此功能的大多数用途是 如果希望最大程度地使用OIBITS,则为它们添加明确的界限。变化不大,因为带有私有字段的结构已经有点存在这种情况了
- 在两种情况下,对私有实施方式的更改都可能会更改OIBIT是否为 实施与否。
- 在两种情况下,如果没有文档工具,OIBIT impls的存在是不可见的
- 在两种情况下,您只能断言OIBIT impls的存在 通过向API或板条箱的测试套件添加显式特征范围。
实际上,OIBIT的主要目的是削减 跨越抽象障碍,并提供有关类型的信息,而无需 类型的作者必须明确选择加入。
但是,这意味着它必须被视为对 更改具有抽象返回类型的函数,以消除OIBIT隐含的方式, 这可能是个问题。 (如上所述,
struct
就是这种情况 定义。)但是由于使用的OIBIT的数量相对较少,因此推导了返回类型 在功能主体中以及是否会发生这种破损的推理有 被认为是可管理的工作量。
另请参阅: