匹配实现相同特质的枚举项元组

huangapple go评论73阅读模式
英文:

Match enum item tuples that implement the same trait

问题

I have written a CLI program using clap.

In it, I have several structs and enums containing these structs for the specific sub-commands.

I now ran into the issue of a certain type of code duplication and wonder, whether the Rust language has some feature, that I am unaware of, that can deduplicate this.

Here's a MRE:

pub trait Runner {
    fn run(&self);
}

pub struct Foo;

impl Runner for Foo {
    fn run(&self) {
        println!("Fooing");
    }
}

pub struct Bar;

impl Runner for Bar {
    fn run(&self) {
        println!("Baring");
    }
}

pub enum Action {
    DoFoo(Foo),
    DoBar(Bar),
}

impl Runner for Action {
    fn run(&self) {
        match self {
            Self::DoFoo(runner) => runner.run(),
            Self::DoBar(runner) => runner.run(),
        }
    }
}

As you can see, the pattern matching on <Action as Runner>::run() has mostly the same code on all variants, since all of the enum variant's tuples contain exactly one element that implements Runner.

In this MRE it's quite trivial, but my actual code has currently seven variants.

Is it possible in Rust to deduplicate this pattern matching and use the fact that all variants contain a struct with the same trait?

英文:

I have written a CLI program using clap.

In it, I have several structs and enums containing these structs for the specific sub-commands.

I now ran into the issue of a certain type of code duplication and wonder, whether the Rust language has some feature, that I am unaware of, that can deduplicate this.

Here's a MRE:

pub trait Runner {
    fn run(&amp;self);
}

pub struct Foo;

impl Runner for Foo {
    fn run(&amp;self) {
        println!(&quot;Fooing&quot;);
    }
}

pub struct Bar;

impl Runner for Bar {
    fn run(&amp;self) {
        println!(&quot;Baring&quot;);
    }
}

pub enum Action {
    DoFoo(Foo),
    DoBar(Bar),
}

impl Runner for Action {
    fn run(&amp;self) {
        match self {
            Self::DoFoo(runner) =&gt; runner.run(),
            Self::DoBar(runner) =&gt; runner.run(),
        }
    }
}

As you can see, the pattern matching on &lt;Action as Runner&gt;::run() has mostly the same code on all variants, since all of the enum variant's tuples contain exactly one element that implements Runner.

In this MRE it's quite trivial, but my actual code has currently seven variants.

Is it possible in Rust to deduplicate this pattern matching and use the fact that all variants contain a struct with the same trait?

答案1

得分: 5

当尝试使用枚举来模仿动态分发方法(依赖于 dyn 特性)时,enum_dispatch crate 自动化了对特性中每个方法的系统转发的样板代码。

以下是一个基于你的示例的简单示例。
在你打算为多个类型实现的特性上使用 #[enum_dispatch]
#[enum_dispatch(Runner)] 指示以下枚举的每个变体都是实现了该特性的类型,然后会自动生成每个变体的所有特性方法的转发(就像你手动编写的那样,每个方法都是一个 match 语句,按顺序考虑所有变体以进行调用转发)。

use enum_dispatch::enum_dispatch;

#[enum_dispatch]
pub trait Runner {
    fn run(&self);
}

pub struct Foo;

impl Runner for Foo {
    fn run(&self) {
        println!("Fooing");
    }
}

pub struct Bar;

impl Runner for Bar {
    fn run(&self) {
        println!("Baring");
    }
}

#[enum_dispatch(Runner)]
pub enum Action {
    Foo,
    Bar,
}

fn main() {
    let actions = [
        Action::from(Foo {}),
        Action::from(Bar {}),
        Action::from(Bar {}),
        Action::from(Foo {}),
    ];
    for a in actions.iter() {
        a.run();
    }
}
/*
Fooing
Baring
Baring
Fooing
*/
英文:

When trying to mimic the dynamic-dispatch approach (relying on dyn traits) with enums, the enum_dispatch crate automates the boilerplate for the systematic forwarding of each method in the trait.

Here is a simple example, based on yours.
#[enum_dispatch] is used on the trait you intend to implement for many of your types.
#[enum_dispatch(Runner)] instructs the following enum that each of its variants are types that implement this trait, then the forwarding of every trait method is automatically generated for all of these variants, as you would do manually (each method is a match statement considering all the variants in order to forward the call on it).

use enum_dispatch::enum_dispatch;

#[enum_dispatch]
pub trait Runner {
    fn run(&amp;self);
}

pub struct Foo;

impl Runner for Foo {
    fn run(&amp;self) {
        println!(&quot;Fooing&quot;);
    }
}

pub struct Bar;

impl Runner for Bar {
    fn run(&amp;self) {
        println!(&quot;Baring&quot;);
    }
}

#[enum_dispatch(Runner)]
pub enum Action {
    Foo,
    Bar,
}

fn main() {
    let actions = [
        Action::from(Foo {}),
        Action::from(Bar {}),
        Action::from(Bar {}),
        Action::from(Foo {}),
    ];
    for a in actions.iter() {
        a.run();
    }
}
/*
Fooing
Baring
Baring
Fooing
*/

huangapple
  • 本文由 发表于 2023年6月26日 20:55:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76556901.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定