Rust中将子特征的特征对象转换为父特征的特征对象

总字数:1116字,预计阅读时间 01分 51秒。

这辈子就是被Rust编译器害了.jpg

背景

还是在开发同上一篇相同的项目——一个编译器。在编写语法分析构建抽象语法树的过程中设计了这样一种抽象语法树的数据结构:每一个抽象语法树节点都实现了一个基础的语法树节点特征SyntaxNode,同时每个可以参加运算、有返回类型的语法树节点都需要实现ExpressionSyntaxNode特征,该特征是SyntaxNode特征的子特征。因此,从特征对象Rc<dyn ExpressionSyntaxNode>到特征对象Rc<dyn SyntaxNode>的转换就成为在语法树构建过程中必然会遇到的问题。

这种数据结构的设计就是一个非常具有面向对象特色的设计思路,但是我们伟大的Rust(目前)却不支持这种特征对象的转换。这种转换在Rust语言内部称作trait-upcasting,已经在RFC3324中完成了定义,但其的实现从2021年开始一直到现在都处于unstable的状态,需要在nightly版本的编译器中开启#![feature(trait_upcasting)]。具体来说,这个特点允许当特征Bar 是另一个特征Foo的子特征Bar : Foo时是一个特征对象dyn Bar被转换为特征对象dyn Foo

当前条件下的实现方法

虽然我们可以在使用nightly编译器的条件下使用feature开关抢先实用这个功能,但是应该没有人会在生产环境下使用nightly编译器罢。

所以我们需要一个在当前环境下可以使用的解决方法。解决的思路是设计一个类型转换的辅助特征CastHelper,这个特征就提供了需要的转换方法:

trait CastHelper {
    fn cast(&self) -> &dyn Foo;
}

然后在定义Bar特征时,让CastHelper也成为Bar特征的超特征。

trait Bar : Foo + CastHelper {
    // Other method.
}

接下来使用泛型的方式为所有实现了Bar 的结构体实现CastHelper

impl<T> CastHelper for T
where
	T : Bar + 'static
{
    fn cast(&self) -> &dyn Foo {
        self as _
    }
}

CastHelper中也可以定义到Rc<dyn Foo>Box<dyn Foo>等特征对象的转换。

所有的实现代码如下:

trait Foo {}
trait Bar: Foo + CastHelper {}

trait CastHelper {
    fn cast(&self) -> &dyn Foo;
}

impl<T> CastHelper for T
where
	T : Bar + 'static
{
    fn cast(&self) -> &dyn Foo {
        self as _
    }
}
文章作者:Ricardo Ren
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,诸位读者如有兴趣可任意转载,不必征询许可,但请注明“转载自 Ricardo's Blog ”。

2021 - 2025 © Ricardo Ren ,由 .NET 9.0.1 驱动。

蜀ICP备2022004429号-1