在Rust中使用枚举实现动态多态 [英] Using enums for dynamic polymorphism in Rust

查看:78
本文介绍了在Rust中使用枚举实现动态多态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当已经知道某些需要动态多态的代码中涉及的所有有限数量的类型时,与使用 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中使用枚举实现动态多态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆