问题描述
当已经知道某些需要动态多态的代码中涉及的所有有限数量的类型时,与使用 Box
相比,使用 enum
可能会更好地提高性能.使用动态内存分配,您还需要使用具有虚拟函数调用的特征对象.
When one already knows all the finite number of types involved in some code which needs dynamic polymorphism, using enum
can be better for performances compared to using Box
since the latter uses dynamic memory allocation and you'll need to use trait objects which have virtual function call as well.
也就是说,与使用 std :: variant
和 std :: visit
的C ++中的等效代码相比,Rust在这种情况下看起来涉及更多样板代码,至少对我来说(我还没有学会使用过程宏).在此举一些例子:我有一堆 struct
类型:
That said, compared to the equivalent code in C++ using std::variant
and std::visit
, looks like Rust in this scenario has more boilerplate coding involved, at least for me (I have not yet learned to use procedural macros). Making some example here: I have a bunch of struct
types:
struct A {
// ...
}
struct B {
// ...
}
// ...
struct Z {
// ...
}
它们都实现了特征 AlphabetLetter
:
trait AlphabetLetter {
fn some_function(&self);
}
由于所涉及的类型集是已知的且受限制的,因此我想使用 enum
:
Since the set of types involved is known and limited, I want to use enum
:
enum Letter {
AVariant(A),
BVariant(B),
// ...
ZVariant(Z),
}
在这里,我们已经有了第一个样板:我需要为每个涉及的类型变量为 enum
值添加一个名称.但是真正的问题是: enum
Letter
本身就是一个 AlphabetLetter
,它仅表示我们在运行时不知道它是哪个字母的事实..因此,我开始为其实现特征:
Already here we have the first boilerplate: I need to add a name for the enum
value for every type variant involved. But the real issue is: enum
Letter
is itself an AlphabetLetter
, it just represent the fact that we do not know at runtime which letter it is. So I started implementing the trait for it:
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 ++中,由于使用了通用的lambda,因此可以只 std :: visit
一个 std :: variant
,并且它是一个内衬.不用手动为特征X中的每个变体X的每个变体编写所有与模式匹配的模式匹配,该如何做?
And yes, this can become easily a lot of code, but I found no other way of doing it. In C++, thanks to generic lambdas, one can just std::visit
a std::variant
and it's a one liner. How can I do the same without manually writing all the pattern matching for every function in the traits X every variant in the enum
?
推荐答案
您可以通过示例使用宏(而不是程序宏)来避免样板:
You can use a macro by example (rather than a procedural macro) to avoid the boilerplate:
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(),
)*
}
}
}
};
}
然后您将其称为生成所有内容:
Then you call it to generate everything:
make_alphabet!(A, B, C, ..., Z);
现在,您只要有 letter:Letter
:
letter.some_function();
对于不需要对所有变体都进行操作的方法,可以在外部使用 impl
.
For methods that do not need to operate on all the variants, you can have an impl
outside.
这篇关于在Rust中使用枚举实现动态多态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!