Box box box • baby steps

这篇文章是作者关于在带有异步函数的特性中支持dyn Trait的思考,旨在找到既方便又灵活的使用dyn Trait的方法,具体内容如下:

  • 背景与挑战:支持async fndyn 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>,然后可以等待它。但这一设计改变了Traitdyn 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 Traitdyn Trait类型不需要指定关联类型,dyn compatible方法作为固有方法暴露在dyn Trait类型上,指定所有关联类型的dyn Trait被视为实现Trait
  • box关键字

    • boxed 表达式.box运算符可用于将值放入盒子中,允许编译器保证不创建中间值,对于返回无大小类型的函数,这是处理异步函数返回dyn Future的一种方式。
    • boxed 异步函数以允许递归:当前无法直接编写递归的异步函数,需要将返回值改为Pin<Box<impl Future<Output = u32>>>,如果能直接请求则更方便。
    • boxed 结构体可以是递归的:递归结构体在 Rust 中会报错,引入Box可以解决问题,但会导致结构体的头部和后续节点存储方式不同,添加box关键字可以使结构体的声明更方便,并能完全透明地进行模式匹配。
    • boxed 枚举可以是递归且大小合适:枚举不能自身引用,添加box关键字可以方便地声明递归枚举,并且不同枚举变体可以根据需要分配不同大小的空间,还可以对特定枚举变体进行装箱,避免枚举大小不平衡。
    • boxed 模式和类型:在其他地方启用box后,也应该允许在模式中使用box
  • 常见问题解答

    • Box::new(v)v.box行为不同,v.box是更优形式,未来可考虑使Box::new等构造函数更优化。
    • 特殊处理box而不处理其他智能指针是有原因的,box代表完全所有权,与TRc<T>等智能指针不同,未来可将box概念推广到其他指针。
    • 这些box提议与“Rust 的灵魂”相契合,将box关键字移到声明处更合适,除了通过dyn trait调用异步函数外,其他部分都符合。
    • 可以将dyn Trait实现Trait声明为dyn关键字,例如box dyn trait Signal,可能会自动在幕后包含Box,并在调用async fn时自动装箱结果。

总之,通过这些box相关的提议和对dyn Trait的改进,有望解决当前dyn Trait使用中的一些问题,提高 Rust 的可用性。

阅读 8
0 条评论