我怎样才能拥有一组因关联类型不同而不同的对象? [英] How can I have a collection of objects that differ by their associated type?

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

问题描述

我有一个程序,它涉及检查复杂的数据结构以查看它是否有任何缺陷.(这很复杂,所以我发布了示例代码.)所有检查都彼此无关,并且都有自己的模块和测试.

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).

我有 CheckError 的特征:

I have traits for Check and Error:

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>.但是,我收到此错误:

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

因为 Error trait 中的 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 对象的向量,不如使用所有具有相同类型的闭包向量,抽象掉被调用的函数:

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 对象的向量,它还允许 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天全站免登陆