Background
I'm implementing a distributed consensus protocol, and a common operation is to use a back-track-able predicate to construct a set or the like. Below is the specific error I'm running into, however, I believe this class of problems is something more general than just this specific case.
The Issue
So I'm bumping into an interesting lifetimes issue. Let's see here...
First things first, I have a Value, which is a non-static trait that is not a trait object. Here's the definition:
pub trait Value: Eq + Ord + fmt::Debug {
fn combine(this: Self, that: Self, slot_id: SlotId) -> Self;
}
Next up, I have another trait, Predicate, which has one member function. It takes a generic Message<T> (a struct that contains a Value, among other things), and optionally returns a new predicate:
pub trait Predicate<T: Value>: fmt::Debug {
fn test(self, message: &Message<T>) -> Option<Box<dyn Predicate<T>>>;
}
Last but not least, I have FnPredicate, which applies a function as a predicate, and returns itself if that function returns true:
pub struct FnPredicate<T: Value>(Box<fn(&Message<T>) -> bool>);
impl<T: Value> Predicate<T> for FnPredicate<T> {
fn test(self, message: &Message<T>) -> Option<Box<dyn Predicate<T>>> {
return if (self.0)(message) {
Some(Box::new(self))
} else {
None
}
}
}
Right now, I'm currently getting a lifetime error when I box self in Some(Box::new(self)):
the parameter type `T` may not live long enough
...so that the type `FnPredicate<T>` will meet its required lifetime bounds
Any tips? I'm getting a serious code-smell right off the bat, but I haven't honestly explored the solution space that much. One more quick thing - rustc gives me this help message:
help: consider adding an explicit lifetime bound...: `T: 'static +`
But value is non-static, so this would result in further issues down the road. Thoughts y'all? Thanks in advance!
Generalities
I believe these... class of problems are something more general than just this specific case.
Whenever I try to use traits to emulate 'open' enums - i.e. sets of related types that implement the same interface - I usually run into what seems like a wall of errors no matter which direction I go. In the past, I've resolved this issue by switching to 'closed' enums, turning traits and their implementations into enums.
This seems related to the expression problem. Ignoring the example above, what is the idiomatic way to resolve the expression problem in Rust and what general patterns exist when working with traits and trait objects?
I feel like I have a solid grasp on traits when creating basic interfaces, but anything self-referential or dyn-related quickly gets shuts down. I understand the why, just not what general solutions exist.
Thanks!