I'm looking for a way to make a struct with a generic type parameter, that has some methods regardless, and even more methods when a trait is implemented on the generic type parameter.
The problem is that the internal implementation relies on a dyn trait object.
Here is a simplified version of the issue and the way I'd solve it with impl specialization:
trait Lattice {
fn join(&self) -> Self;
}
trait NodeTrait<T> {
/// This method can do something useful for any type of T
fn something_basic(&self);
/// This method's impl would depend on T: Lattice, and the method could be moved to a sub-trait
fn join_dyn(&self) -> NodeWrapper<T>;
}
struct NodeWrapper<T>(Box<dyn NodeTrait<T>>);
impl<T> Lattice for NodeWrapper<T> where T:Lattice
{
fn join(&self) -> Self {
self.0.join_dyn()
}
}
struct Node<T>(Vec<T>);
impl<T: 'static> NodeTrait<T> for Node<T> where T: Lattice {
fn something_basic(&self) {
println!("something");
}
fn join_dyn(&self) -> NodeWrapper<T> {
NodeWrapper(Box::new(Self(vec![])))
}
}
impl<T: 'static> NodeTrait<T> for Node<T> {
fn something_basic(&self) {
println!("something");
}
fn join_dyn(&self) -> NodeWrapper<T> {
unreachable!()
}
}
impl Lattice for f32 {
fn join(&self) -> Self {
*self
}
}
fn main() {
let yes: NodeWrapper::<f32> = NodeWrapper(Box::new(Node(vec![])));
let no: NodeWrapper::<i32> = NodeWrapper(Box::new(Node(vec![])));
}
I know I could separate the join_dyn
method into another trait (LatticeNodeTrait
) and create a runtime accessor like fn lattice_node(&self) -> Option<&dyn LatticeNodeTrait>
to access the implementation. But I need to call this in the middle of an inner-loop, and this comes with a nasty performance penalty. (the compiler doesn't seem to be able to do anything to optimize the dynamic dispatch)
Are there any creative contortions to make something like this work at compile-time in safe Rust (or Rust 2024. Maybe it wait until October to deploy)
Thank you.