当每个 arm 返回带有类型参数的类型的不同组合时,如何减少 2 个嵌套匹配语句

How can I reduce 2 nested match statements when each arm returns a different combination of a type with a type argument

提问人:exocortex 提问时间:10/28/2023 更新时间:10/29/2023 访问量:47

问:

我的问题:我有一个返回-object的函数。假设我在级别 1 上有实现此目的的可能类型,并且我希望通过 match 语句访问这两种类型。但是现在这 2 种可能的类型也是通用的(但必须实现),并且我想在 2 级使用可能的类型。现在,我可以有一个 4 个臂的匹配项,也可以有 2 个嵌套的匹配项,每个组有 2 个臂。我必须写 4 个语句来初始化它。这很简单。但是,如果我在级别 1(实现 Trait_1)有类型,在级别 2 有 20 个类型,实现其他类型,我最终会编写 300 个不同的初始化!这是很多工作,而且我可能会犯一些错误。Box<dyn Trait_1>N=2Trait_1Trait_2M=2BoxN=15Trait_2

我写了一个简单的例子来说明我的问题,其中有 2 个嵌套匹配语句,每个语句有 2 个臂,总共导致 4 个初始化(我省略了 Trait 实现)

pub trait Trait_1 {}
pub trait Trait_2 {}

pub struct SingleContainer<T: Trait_2> {
    item: T,
}

pub struct DoubleContainer<T: Trait_2> {
    item_1: T,
    item_2: T,
}

// -> impl Trait_1 for SingleContainer and DoubleContainer

struct X {
    number: f64,
}

struct Y {
    number: u32,
}

// -> impl Trait_2 for X and Y

fn main() {
    let a = // determined at runtime
    let b = // determined at runtime

    let thing = give_thing(a, b);
}

fn give_thing(thing_number: usize, thing_name: &str) -> Box<dyn Trait_1> {
    match thing_number {
        1 => match thing_name {
            "X" => Box::new(SingleContainer::<X>::default()),
            "Y" => Box::new(SingleContainer::<Y>::default()),
            _ => todo!(),
        },
        2 => match thing_name {
            "X" => Box::new(DoubleContainer::<X>::default()),
            "Y" => Box::new(DoubleContainer::<Y>::default()),
            _ => todo!(),
        },
        _ => todo!(),
    }
}

有没有更好的方法可以做到这一点?我很难从另一个角度看待问题,因为这些东西中的每一个都是不同的类型。我希望在编译时(SingleContainer,DoubleContainer)和(X,Y)的每个可能的组合都“存在”,因此将得到优化。如果有更好的方法可以实现这一目标,请告诉我!我基本上只需要一个返回一个获得 2 个参数的 -object 的函数。但是由于每个参数组合返回不同的类型,我感到非常困惑!Box<dyn MyTrait>

编辑:在这种情况下,一揽子实现会以任何方式提供帮助吗?

(如果有人有更好的方式来表达我的问题,请随时纠正我!

泛型 rust 嵌套 匹配

评论

0赞 Jmb 10/28/2023
根据您的用例,有两种解决方案:像您一样,或者一个(例如,如果您只有两个案例,或者如果您有更多案例,则自定义)。Box<dyn Trait>enumenum

答:

1赞 Chayim Friedman 10/29/2023 #1

在这里,一个小类型的元编程可以提供帮助。

trait MakeTrait1 {
    type Make<T: Trait_2 + Default + 'static>: Trait_1 + Default + 'static;
}

struct MakeSingleContainer;
impl MakeTrait1 for MakeSingleContainer {
    type Make<T: Trait_2 + Default + 'static> = SingleContainer<T>;
}

struct MakeDoubleContainer;
impl MakeTrait1 for MakeDoubleContainer {
    type Make<T: Trait_2 + Default + 'static> = DoubleContainer<T>;
}

fn make_trait1<T: MakeTrait1>(thing_name: &str) -> Box<dyn Trait_1> {
    match thing_name {
        "X" => Box::new(T::Make::<X>::default()),
        "Y" => Box::new(T::Make::<Y>::default()),
        _ => todo!(),
    }
}

fn give_thing(thing_number: usize, thing_name: &str) -> Box<dyn Trait_1> {
    match thing_number {
        1 => make_trait1::<MakeSingleContainer>(thing_name),
        2 => make_trait1::<MakeDoubleContainer>(thing_name),
        _ => todo!(),
    }
}

游乐场

我们基本上创建了一个类型工厂,该工厂为内部类型创建外部类型。然后我们有一个函数,它基于这个类型级工厂做出运行时决策,剩下要做的就是有一个函数,在每个工厂中调用这个函数。

评论

0赞 exocortex 10/30/2023
谢谢!我想我花了相当长的时间才理解你的代码。但现在我想我明白了:-)因此,在运行时可以调用 N * M 不同的类型组合(N 是外部类型,M 是内部类型)。但是它们在编译时“存在”,因此会被优化,对吧?
0赞 Chayim Friedman 10/30/2023
@exocortex 这就像你手写的一样,只是编译器正在为你做这件事。
0赞 exocortex 11/2/2023
我意识到这在运行时是动态的,对吧?
0赞 Chayim Friedman 11/2/2023
@exocortex 是的,当然。