这篇文章是作者关于在带有异步函数的特性中支持dyn Trait
的思考,旨在找到既方便又灵活的使用dyn Trait
的方法,具体内容如下:
- 背景与挑战:支持
async fn
在dyn traits
中是一个棘手的平衡行为,挑战在于协调人们喜爱的 Rust 的两个关键方面——表达高级、高效代码的能力和关注揭示低级细节的能力。以Signal
trait 为例,在 Rust 中可以编写接受impl Signal
的函数,但要使用dyn Signal
则会出错,因为编译器需要知道signal
返回的未来类型的大小以便进行等待,常见的解决方案是将未来值装箱,但这会增加开销并限制某些应用。 - “box box box”设计:最近作者与[Michal Goulet]在一系列对话中重新研究了这个问题,提出了“box box box”设计。该设计从[调用站点选择]方法开始,当通过
dyn Trait
调用异步函数时,会得到一个dyn Future
,需要将其存储在某个地方,最简单的方法是使用.box
运算符将其存储在一个盒子中,得到Box<dyn Future>
,然后可以等待它。但这一设计改变了Trait
和dyn Trait
的关系,并引入了.box
运算符,可能会带来一些根本性的变化。 核心提案与变化:
- Change 0:无大小返回值方法:为每个特性
Foo
,在dyn Foo
上添加固有方法,反映其方法。对于dyn Foo
,只允许所有方法都是dyn compatible
的情况,有些非dyn-compatible
的方法会以修改后的签名添加。 - Change 1:Dyn 兼容性:
dyn Trait
实现Trait
有时很强大,但并不完全符合预期,会限制一些场景。例如对于含有非dyn compatible
方法的ReportError
trait,当前规则不允许某些函数使用dyn ReportError
。这与通过dyn
trait 调用异步函数时需要.box
运算符的要求不兼容。同时,列出关联类型的要求也有局限性,如对于dyn Iterator
,需要指定Item
类型,但有时不需要知道Item
类型也能调用某些方法。 - 提议:允许在特性不完全
dyn compatible
的情况下使用dyn Trait
,dyn Trait
类型不需要指定关联类型,dyn compatible
方法作为固有方法暴露在dyn Trait
类型上,指定所有关联类型的dyn Trait
被视为实现Trait
。
- Change 0:无大小返回值方法:为每个特性
box
关键字:- boxed 表达式:
.box
运算符可用于将值放入盒子中,允许编译器保证不创建中间值,对于返回无大小类型的函数,这是处理异步函数返回dyn Future
的一种方式。 - boxed 异步函数以允许递归:当前无法直接编写递归的异步函数,需要将返回值改为
Pin<Box<impl Future<Output = u32>>>
,如果能直接请求则更方便。 - boxed 结构体可以是递归的:递归结构体在 Rust 中会报错,引入
Box
可以解决问题,但会导致结构体的头部和后续节点存储方式不同,添加box
关键字可以使结构体的声明更方便,并能完全透明地进行模式匹配。 - boxed 枚举可以是递归且大小合适:枚举不能自身引用,添加
box
关键字可以方便地声明递归枚举,并且不同枚举变体可以根据需要分配不同大小的空间,还可以对特定枚举变体进行装箱,避免枚举大小不平衡。 - boxed 模式和类型:在其他地方启用
box
后,也应该允许在模式中使用box
。
- boxed 表达式:
常见问题解答:
Box::new(v)
和v.box
行为不同,v.box
是更优形式,未来可考虑使Box::new
等构造函数更优化。- 特殊处理
box
而不处理其他智能指针是有原因的,box
代表完全所有权,与T
和Rc<T>
等智能指针不同,未来可将box
概念推广到其他指针。 - 这些
box
提议与“Rust 的灵魂”相契合,将box
关键字移到声明处更合适,除了通过dyn trait
调用异步函数外,其他部分都符合。 - 可以将
dyn Trait
实现Trait
声明为dyn
关键字,例如box dyn trait Signal
,可能会自动在幕后包含Box
,并在调用async fn
时自动装箱结果。
总之,通过这些box
相关的提议和对dyn Trait
的改进,有望解决当前dyn Trait
使用中的一些问题,提高 Rust 的可用性。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。