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

nya*_*108 9 enums variant rust run-time-polymorphism

当人们已经知道某些需要动态多态性的代码中涉及的所有有限数量的类型时,与 usingenum相比,using 的性能会更好,Box因为后者使用动态内存分配,并且您需要使用具有虚拟函数调用的特征对象出色地。

也就是说,与使用std::variantand的 C++ 中的等效代码相比std::visit,在这种情况下,Rust 看起来涉及更多样板代码,至少对我来说是这样(我还没有学会使用过程宏)。在这里举一些例子:我有很多struct类型:

struct A {
    // ...
}

struct B {
    // ...
}

// ...

struct Z {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

他们都实现了AlphabetLetter以下特征:

trait AlphabetLetter {
    fn some_function(&self);
}
Run Code Online (Sandbox Code Playgroud)

由于涉及的类型集是已知且有限的,我想使用enum

enum Letter {
    AVariant(A),
    BVariant(B),
    // ...
    ZVariant(Z),
}
Run Code Online (Sandbox Code Playgroud)

这里我们已经有了第一个样板:我需要为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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

是的,这可以很容易地变成很多代码,但我发现没有其他方法可以做到这一点。在 C++ 中,由于通用 lambda,我们可以只使用std::visitastd::variant并且它是一个单行代码。我怎样才能做到同样的事情,而不需要手动为特征 X 中的每个变体中的每个函数编写所有模式匹配enum

Aco*_*orn 5

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

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(),
                    )*
                }
            }
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

然后你调用它来生成所有内容:

make_alphabet!(A, B, C, ..., Z);
Run Code Online (Sandbox Code Playgroud)

现在您可以随时访问它letter: Letter

letter.some_function();
Run Code Online (Sandbox Code Playgroud)

对于不需要对所有变体进行操作的方法,您可以使用impl外部。