我如何才能获得一组根据其关联类型而不同的对象? [英] How can I have a collection of objects that differ by their associated type?

查看:59
本文介绍了我如何才能获得一组根据其关联类型而不同的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个程序,其中涉及检查复杂的数据结构以查看其是否有任何缺陷。 (这很复杂,因此我要发布示例代码。)所有检查都不相互关联,并且都具有自己的模块和测试。

I have a program that involves examining a complex data structure to see if it has any defects. (It's quite complicated, so I'm posting example code.) All of the checks are unrelated to each other, and will all have their own modules and tests.

更多重要的是,每张支票都有自己的错误类型,其中包含有关每个数字的支票失败方式的不同信息。我这样做不是只返回错误字符串,而是可以测试错误(这就是 Error 依赖 PartialEq )。

More importantly, each check has its own error type that contains different information about how the check failed for each number. I'm doing it this way instead of just returning an error string so I can test the errors (it's why Error relies on PartialEq).

我对检查具有特征错误

trait Check {
    type Error;
    fn check_number(&self, number: i32) -> Option<Self::Error>;
}

trait Error: std::fmt::Debug + PartialEq {
    fn description(&self) -> String;
}

还有两个示例检查,以及它们的错误结构。在此示例中,如果数字为负数甚至偶数,我想显示错误:

And two example checks, with their error structs. In this example, I want to show errors if a number is negative or even:


#[derive(PartialEq, Debug)]
struct EvenError {
    number: i32,
}
struct EvenCheck;

impl Check for EvenCheck {
    type Error = EvenError;

    fn check_number(&self, number: i32) -> Option<EvenError> {
        if number < 0 {
            Some(EvenError { number: number })
        } else {
            None
        }
    }
}

impl Error for EvenError {
    fn description(&self) -> String {
        format!("{} is even", self.number)
    }
}

#[derive(PartialEq, Debug)]
struct NegativeError {
    number: i32,
}
struct NegativeCheck;

impl Check for NegativeCheck {
    type Error = NegativeError;

    fn check_number(&self, number: i32) -> Option<NegativeError> {
        if number < 0 {
            Some(NegativeError { number: number })
        } else {
            None
        }
    }
}

impl Error for NegativeError {
    fn description(&self) -> String {
        format!("{} is negative", self.number)
    }
}

我知道在此示例中,两个结构看起来相同,但是在我的代码中,有许多不同的结构,因此我无法合并它们。最后,使用示例 main 函数来说明我要执行的操作:

I know that in this example, the two structs look identical, but in my code, there are many different structs, so I can't merge them. Lastly, an example main function, to illustrate the kind of thing I want to do:

fn main() {
    let numbers = vec![1, -4, 64, -25];
    let checks = vec![
        Box::new(EvenCheck) as Box<Check<Error = Error>>,
        Box::new(NegativeCheck) as Box<Check<Error = Error>>,
    ]; // What should I put for this Vec's type?

    for number in numbers {
        for check in checks {
            if let Some(error) = check.check_number(number) {
                println!("{:?} - {}", error, error.description())
            }
        }
    }
}

您可以在 Rust游乐场

我要解决的最接近的事情是删除关联的类型,并使检查返回 Option< Box< Error>> 。但是,我却收到此错误:

The closest thing I've come to a solution is to remove the associated types and have the checks return Option<Box<Error>>. However, I get this error instead:

error[E0038]: the trait `Error` cannot be made into an object
 --> src/main.rs:4:55
  |
4 |     fn check_number(&self, number: i32) -> Option<Box<Error>>;
  |                                                       ^^^^^ the trait `Error` cannot be made into an object
  |
  = note: the trait cannot use `Self` as a type parameter in the supertraits or where-clauses


$ b $中使用`Self`作为类型参数b

因为 Error 特性的 PartialEq 。到目前为止,Rust对我来说一直很棒,我真的希望我能够使类型系统支持这样的东西!

because of the PartialEq in the Error trait. Rust has been great to me thus far, and I really hope I'm able to bend the type system into supporting something like this!

推荐答案

我最终找到了一种令我满意的方法。而不是拥有 Box< Check< ???> 对象的向量,而是要有一个全部具有相同类型的闭包向量,从而抽象出

I eventually found a way to do it that I'm happy with. Instead of having a vector of Box<Check<???>> objects, have a vector of closures that all have the same type, abstracting away the very functions that get called:

fn main() {
    type Probe = Box<Fn(i32) -> Option<Box<Error>>>;

    let numbers: Vec<i32> = vec![ 1, -4, 64, -25 ];
    let checks = vec![
        Box::new(|num| EvenCheck.check_number(num).map(|u| Box::new(u) as Box<Error>)) as Probe,
        Box::new(|num| NegativeCheck.check_number(num).map(|u| Box::new(u) as Box<Error>)) as Probe,
    ];

    for number in numbers {
        for check in checks.iter() {
            if let Some(error) = check(number) {
                println!("{}", error.description());
            }
        }
    }
}

不这仅允许返回 Box< Error> 对象的向量,它允许 Check 对象提供他们自己的错误关联类型,不需要实现 PartialEq 。多个 as 看起来有点凌乱,但总体上来说 并不坏。

Not only does this allow for a vector of Box<Error> objects to be returned, it allows the Check objects to provide their own Error associated type which doesn't need to implement PartialEq. The multiple ases look a little messy, but on the whole it's not that bad.

这篇关于我如何才能获得一组根据其关联类型而不同的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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