rust 利用什么模式来使用不同嵌套泛型类型/特征对象的Vec?

qv7cva1a  于 2022-12-04  发布在  其他
关注(0)|答案(1)|浏览(74)

我正在尝试实现一种模式,在这种模式中,不同的 * 处理器 * 可以规定它们所采用的输入类型,并产生统一的输出(目前是一种固定类型,但我希望在当前的实现工作后使其通用)。
下面是一个最小的示例:

use std::convert::From;

use processor::NoOpProcessor;

use self::{
    input::{Input, InputStore},
    output::UnifiedOutput,
    processor::{MultiplierProcessor, Processor, StringProcessor},
};

mod input {
    use std::collections::HashMap;

    #[derive(Debug)]
    pub struct Input<T>(pub T);

    #[derive(Default)]
    pub struct InputStore(HashMap<String, String>);

    impl InputStore {
        pub fn insert<K, V>(mut self, key: K, value: V) -> Self
        where
            K: ToString,
            V: ToString,
        {
            let key = key.to_string();
            let value = value.to_string();
            self.0.insert(key, value);

            self
        }

        pub fn get<K, V>(&self, key: K) -> Option<Input<V>>
        where
            K: ToString,
            for<'a> &'a String: Into<V>,
        {
            let key = key.to_string();
            self.0.get(&key).map(|value| Input(value.into()))
        }
    }
}

mod processor {
    use super::{input::Input, output::UnifiedOutput};

    use super::I32Input;

    pub struct NoOpProcessor;

    pub trait Processor {
        type I;
        fn process(&self, input: &Input<Self::I>) -> UnifiedOutput;
    }

    impl Processor for NoOpProcessor {
        type I = I32Input;
        fn process(&self, input: &Input<Self::I>) -> UnifiedOutput {
            UnifiedOutput(input.0 .0)
        }
    }

    pub struct MultiplierProcessor(pub i32);

    impl Processor for MultiplierProcessor {
        type I = I32Input;
        fn process(&self, input: &Input<Self::I>) -> UnifiedOutput {
            UnifiedOutput(input.0 .0 * self.0)
        }
    }

    pub struct StringProcessor;

    impl Processor for StringProcessor {
        type I = String;
        fn process(&self, input: &Input<Self::I>) -> UnifiedOutput {
            UnifiedOutput(input.0.parse().unwrap())
        }
    }
}

mod output {
    #[derive(Debug)]
    pub struct UnifiedOutput(pub i32);
}

pub fn main() {
    let input_store = InputStore::default()
        .insert("input_a", 123)
        .insert("input_b", 567)
        .insert("input_c", "789");

    let processors = {
        let mut labelled_processors = Vec::new();
        // let mut labelled_processors: Vec<LabelledProcessor<Input<>>> = Vec::new(); // What's the correct type?
        labelled_processors.push(LabelledProcessor("input_a", Box::new(NoOpProcessor)));
        labelled_processors.push(LabelledProcessor(
            "input_b",
            Box::new(MultiplierProcessor(3)),
        ));
        // labelled_processors.push(LabelledProcessor("input_c", Box::new(StringProcessor)));

        labelled_processors
    };

    for processor in processors {
        let output = retrieve_input_and_process(&input_store, processor);
        println!("{:?}", output);
    }
}

#[derive(Debug)]
pub struct I32Input(pub i32);

impl From<&String> for I32Input {
    fn from(s: &String) -> Self {
        Self(s.parse().unwrap())
    }
}

struct LabelledProcessor<I>(&'static str, Box<dyn Processor<I = I>>)
where
    for<'a> &'a String: Into<I>;

fn retrieve_input_and_process<T>(
    store: &InputStore,
    processor: LabelledProcessor<T>,
) -> UnifiedOutput
where
    for<'a> &'a String: Into<T>,
{
    let input = store.get(processor.0).unwrap();
    processor.1.process(&input)
}

// labelled_processors.push(LabelledProcessor("input_c", Box::new(StringProcessor)));被取消注解时,我得到下面的编译错误:

error[E0271]: type mismatch resolving `<attempt2::processor::StringProcessor as attempt2::processor::Processor>::I == attempt2::I32Input`
   --> src/attempt2.rs:101:63
    |
101 |         labelled_processors.push(LabelledProcessor("input_c", Box::new(StringProcessor)));
    |                                                               ^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<attempt2::processor::StringProcessor as attempt2::processor::Processor>::I == attempt2::I32Input`
    |
note: expected this to be `attempt2::I32Input`
   --> src/attempt2.rs:75:18
    |
75  |         type I = String;
    |                  ^^^^^^
    = note: required for the cast from `attempt2::processor::StringProcessor` to the object type `dyn attempt2::processor::Processor<I = attempt2::I32Input>`

我想我已经了解到了足够的信息来“了解”问题所在-labelled_processors vec期望它的所有项都具有相同的 type。我的问题是我不确定如何纠正这一点。我已经尝试更多地利用动态调度(例如将LabelledProcessor更改为struct LabelledProcessor(&'static str, Box<dyn Processor<dyn Input>>);)。然而,这些更改也会螺旋式地导致它们自己的类型系统问题。
我在网上找到的其他答案通常都没有解决嵌套泛型/特性的复杂性问题--答案是let vec_x: Vec<Box<dyn SomeTrait>> ...,这让我想知道是否有一个显而易见的答案可以达到,而我只是错过了,或者是否有一个完全不同的模式可以达到这个目标?
我知道也可能使用枚举,但这意味着所有用例都需要在此模块中捕获,并且它可能无法在外部模块中定义输入/输出/处理器。
此时有点失落。
---编辑---
一些额外要点:

  • 这只是一个例子,所以像InputStore基本上把所有东西都转换成String只是一个实现细节,主要是为了象征“类型需要符合某种特质才能被接受”的概念,我只是为了简单起见选择了String。
5gfr0r5j

5gfr0r5j1#

一个可能的解决方案是使retrieve_input_and_process成为LabelledProcessor的方法,然后将类型隐藏在特征后面:
第一个

相关问题