Conditional implementation on trait existence


I was wondering if it was possible to use in Rust some conditional statements on whereas a trait is implementent on not.

I am writing a numerical solver that is acting on a user model. The model requirements are defined through the Model trait.
During the numerical resolution, it is needed to use the jacobian matrix.
This jacobian matrix can be computed from the Model (this computation being expensive), and sometimes the user can actually implement it. I create for that the Jacobian trait.
Hence I have now two types possibilities, either T: Model or T: Model + Jacobian

The solve function of the Solver is generic, and calls a compute_step method that differs according to the fact the Jacobian trait is implemented or not.
I would like to be able to right the following code but I cannot make it work:

pub trait Model {
// some methods
pub trait Jacobian {
// some other methods
pub struct Solver {}

impl Solver {
  pub fn compute_step<T>(model: T)
      where T: Model
      // Do something expensive
  pub fn compute_step<T>(model: T)
      where T: Model + Jacobian
      // Do something less expensive thanks to the fact that
     // the user implemented the Jacobian trait
  pub fn solve<T>(model: T)
    where T: Model // and maybe also Jacobian
    // some methods calls of the Model trait
   // some other methods calls of the Model trait

The only way I managed to make it work was to name the compute_step methods differently and duplicate the solve method with 2 variants (and two names).
However, I don't like this solution, because the solve method is actually doing a lot of stuff that are common for all models (implementing the Model trait).
There is a single difference at the call to compute_step, this method being different according to the fact an extra trait is implemented or not, the implementation of this trait making the calculation more efficient.

Is there a better way to implement such a feature ?

Thanks a lot.

This is not possible without the unstable specialization feature.

One way would be to always implement the Jacobian trait but include information as to whether or not there is at all a Jacobian to be computed. The Solver could then check this and decide to call the optimized or the expensive stepper.

For example, you could create a separate has_jacobian() function on the type that returns true or false depending on whether the Jacobian is implemented, and then return a dummy Jacobian (or panic) if it isn't. Since this would be an associated function that does not take a runtime argument, the compiler should be able to optimize it away completely. Here's a playground demonstrating that idea. Or maybe even better, signal the existence of the Jacobian using an associated const.

If you want a slightly more elegant solution, you can return an Option, like this, although I don't know if that's going to be optimized out as reliably. It probably is – not sure how expensive your optimization stepping function is, but if it is expensive, then the overhead of a single Option might not matter at all.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.