The problem is that if I want to implement this trait to any other struct, it requires me to implement ValidatorCallback on that struct. And I can't simply do it like this:
impl Validatable for RawProject{
type ValidatorCallback = Fn(&Self)->bool;
}
because of the error
error[E0782]: trait objects must include the `dyn` keyword
and also adding dyn is not helping a lot. How do I solve it?
You need to name a concrete type; traits are not types. Unfortunately, function items and closures don't have nameable types, so your choices are type erasure (dyn Fn) or function pointer indirection:
type ValidatorCallback = fn(&Self) -> bool
type ValidatorCallback = Box<dyn Fn(&Self) -> bool>
type ValidatorCallback = Box<dyn Send + Sync + Fn(&Self) -> bool>
type ValidatorCallback = Arc<dyn Fn(&Self) -> bool>
// ...
The limited gain in flexibility over just hard-coding which type-erased form you want in ValidatoinRule may not be worth it.
It seems like I was not able to avoid using lifetimes here because of my closures which should capture variables.
also here is how looks the actual usage:
let validator = Validator::<RawProject>::new(vec![
ValidationRule {
error_message: |project| format!("project '{}' does not have language tags", project.title),
fallback: ValidationFallback::Error,
validator: Box::new(|project| project.has_any_tags(tag_categoies.get("language").unwrap())),
},
ValidationRule {
error_message: |project| {
format!(
"project '{}' does not have webdev/gamedev tags",
project.title
)
},
fallback: ValidationFallback::Warning,
validator: Box::new(|project| {
project.has_any_tags(&HashSet::from(["webdev".into(), "gamedev".into()]))
}),
},
]);
Not sure if I understand right, but given the code of the OP, it is possible to use generics and avoid type erasure if the struct for which Validatable is to be implemented has a type parameter for the closure: