Enums 使用枚举实现Rust中的动态多态性

Enums 使用枚举实现Rust中的动态多态性,enums,rust,variant,run-time-polymorphism,Enums,Rust,Variant,Run Time Polymorphism,当您已经知道某些代码中需要动态多态性的所有有限类型时,与使用Box相比,使用enum可以获得更好的性能,因为后者使用动态内存分配,并且您还需要使用具有虚拟函数调用的trait对象 上述的代码与C++中的等效代码相比,使用了 STD::变体和 STD::访问< /COD>,看起来Rust在这个场景中有更多的样板代码,至少对我来说(我还没有学会使用过程宏)。这里举一些例子:我有一堆struct类型: struct A { // ... } struct B { // ... }

当您已经知道某些代码中需要动态多态性的所有有限类型时,与使用
Box
相比,使用
enum
可以获得更好的性能,因为后者使用动态内存分配,并且您还需要使用具有虚拟函数调用的trait对象

上述的代码与C++中的等效代码相比,使用了<代码> STD::变体和 STD::访问< /COD>,看起来Rust在这个场景中有更多的样板代码,至少对我来说(我还没有学会使用过程宏)。这里举一些例子:我有一堆

struct
类型:

struct A {
    // ...
}

struct B {
    // ...
}

// ...

struct Z {
    // ...
}
它们都实现了trait
AlphabetLetter
,即:

trait AlphabetLetter {
    fn some_function(&self);
}
由于所涉及的类型集已知且有限,因此我想使用
enum

enum Letter {
    AVariant(A),
    BVariant(B),
    // ...
    ZVariant(Z),
}
这里我们已经有了第一个样板:我需要为涉及的每个类型变量的
enum
值添加一个名称。但真正的问题是:
enum
Letter
本身就是一个
AlphabetLetter
,它只代表了一个事实,即我们在运行时不知道它是哪个字母。所以我开始实施它的特点:

impl AlphabetLetter for Letter {
    fn some_function(&self) {
        match self {
            Letter::AVariant(letter) => letter.some_function();
            Letter::BVariant(letter) => letter.some_function();
            // ...
            Letter::ZVariant(letter) => letter.some_function();
        }
    }
}

是的,这很容易变成很多代码,但我没有找到其他方法。在C++中,由于通用的lambdas,可以只需<代码> STD::访问<代码> A<代码> STD::变体< /代码>,它是一个线性。如何在不手动写入traits X中每个函数的所有模式匹配的情况下执行相同的操作
枚举中的每个变量

您可以通过示例使用宏(而不是程序宏)来避免样板文件:

macro_rules! make_alphabet {
    ($($x:ident),*) => {
        enum Letter {
            $(
                $x($x),
            )*
        }

        impl AlphabetLetter for Letter {
            fn some_function(&self) {
                match self {
                    $(
                        Letter::$x(letter) => letter.some_function(),
                    )*
                }
            }
        }
    };
}
然后调用它来生成所有内容:

make_alphabet!(A, B, C, ..., Z);
现在,只要您有
字母:字母
,就可以随时访问它:

letter.some_function();

对于不需要对所有变量进行操作的方法,您可以在外部设置一个
impl

polymorphic_enum
宏生成一个具有所选名称和变量的枚举,以及另一个具有所选名称的宏。 此生成的宏特定于生成的枚举,因为它对所有变量重复相同的代码块(类似闭包)(与显式操作完全相同)。 它假设所有变体都可以以完全相同的方式使用;因此命名为多态枚举

您不必为每个要以这种方式处理的枚举编写新宏 因为会生成特定于每个特定枚举的宏。 您甚至不必在enum上实现trait(welcome back duck typing;^),但是如果您愿意,可以实现。 您只需以一种不同寻常的方式声明枚举

应该是多态的代码调用类似于 在一个应用程序上为
std::visit()
提供一个通用lambda闭包时,您应该做什么 单<代码> STD::C++中的变体< /代码>(这里没有多个调度)。
trait字母{
fn一些函数(&self)->字符串;
还有别的吗(
&自我,
arg:使用,
) {
println!(“-->{},arg={}”,self.some_function(),arg);
}
}
结构A{
// ...
}
结构B{
// ...
}
// ...
结构Z{
// ...
}
A的impl字母{
fn一些函数(&self)->字符串{
格式!(“A上的某些函数”)
}
}
B的impl字母{
fn一些函数(&self)->字符串{
格式!(“B上的某些函数”)
}
}
// ...
表示Z的impl字母{
fn一些函数(&self)->字符串{
格式!(“Z上的某些函数”)
}
}
宏规则!多态枚举{
($name:ident$macro:ident,$($variant:ident($type:path),)*)=>{
枚举$name{$($variant($type)),*}
宏规则!$macro{
($on:expr,|$with:ident |$body:block)=>{
匹配$on{
$($name::$variant($with)=>body)*
}
}
}
}
}
多态枚举!{
字母使用字母,
先锋派(A),
B变量(B),
// ...
ZVariant(Z),
}
fn main(){
让字母=向量[
字母::AVariant(A{}),
字母::B变量(B{}),
// ...
字母::ZVariant(Z{}),
];
对于字母形式的(i,l)。iter()枚举(){
设msg=use_Letter!(l,| v |{v.some_function()});
println!(“msg={}”,msg);
使用字母!(l,v){
设msg=v.some_函数();
v、 其他的东西((i+1)*msg.len())
});
}
}

没有多态性,也没有动态调度,因为类型是静态已知的。IIUC,C++模板是非类型化的,使得 STD::访问功能类似于使用Ru锈宏(注意C宏是另一回事)。您在C++中为这种便利付出的代价是,在使用时,而不是声明,类型错误被报告,并且在lambda中做不兼容的错误可以是…不透明。(还要注意,过程性宏和声明性宏是不同类型的宏;声明性宏更容易,并且足以使这类事情在没有太多重复的情况下工作。)我认为解决这一问题的最佳方法是局部特征对象。这将消除动态内存分配的需要,在这种情况下,我认为虚拟函数调用与
match
语句的功能非常相似(可能更快)。