У меня есть программа, которая включает в себя изучение сложной структуры данных, чтобы увидеть, есть ли у нее какие-либо дефекты. (Это довольно сложно, поэтому я публикую пример кода.) Все проверки не связаны друг с другом, и все они будут иметь свои собственные модули и тесты.
Что еще более важно, каждая проверка имеет свой собственный тип ошибки, который содержит различную информацию о том, как проверка провалилась для каждого номера. Я делаю это таким образом, вместо того, чтобы просто возвращать строку ошибки, чтобы я мог проверить ошибки (именно поэтому Error
полагается на PartialEq
).
Мой код до сих пор
У меня есть черты для Check
и Error
:
trait Check {
type Error;
fn check_number(&self, number: i32) -> Option<Self::Error>;
}
trait Error: std::fmt::Debug + PartialEq {
fn description(&self) -> String;
}
И два примера проверок, с их ошибочными структурами. В этом примере я хочу показать ошибки, если число отрицательное или даже:
#[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
функции, чтобы проиллюстрировать, что я хочу сделать:
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 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>>
. Однако вместо этого я получаю эту ошибку:
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
из-за PartialEq
в признаке Error
. До сих пор Rust был для меня велик, и я действительно надеюсь, что смогу согнать систему типов в поддержку чего-то подобного!