rustdynamic-dispatch

Remove `Sized` bound from trait whose method takes reference to implementor`<Self>` as an arg


This is a 'sequel' to a previous question of mine, with a more representative example, and a different approach informed by the accepted answer.

Current code state:

My lib crate provides numerical interpolation over multiple dimensionalities and interpolation strategies. The strategy of an Interp1D instance can be changed at runtime (dynamically dispatched) by proving a different Box<dyn Strategy1D> after instantiation.

pub struct Interp1D {
  x: Vec<f64>,
  f_x: Vec<f64>,
  strategy: Box<dyn Strategy1D>,
}

pub trait Strategy1D {
  fn interpolate(
    &self,
    interpolator: &Interp1D,
    point: &[f64; 1],
  ) -> f64;
}

The question

Playground link

I want to support static dispatch to allow for better runtime performance, while still allowing users to supply a Box<dyn Strategy1D> if they want to switch strategies at runtime.

This means my structs will have a generic for the strategy, to support static dispatch:

pub struct Interp1D<S: Strategy1D> {
  x: Vec<f64>,
  f_x: Vec<f64>,
  strategy: S,
}

// as a consequence the trait needs to change:
pub trait Strategy1D: Sized {  // the `Sized` bound here makes this dyn-incompatible
  fn interpolate(
    &self,
    interpolator: &Interp1D<Self>,  // the `Self` here requires `Sized`
    point: &[f64; 1],
  ) -> f64;
}

Adding this:

impl Strategy1D for Box<dyn Strategy1D> {
//                  ^^^^^^^^^^^^^^^^^^^
// error[E0038]: the trait `Strategy1D` cannot be made into an object
  fn interpolate(
    &self,
    interpolator: &Interp1D<Self>,
    point: &[f64; 1],
  ) -> f64 {
    // delegate to boxed thing
    (**self).interpolate(struct, interpolator, point)
  }
}

makes the compiler mad, Strategy1D is not dyn-compatible due to the Sized bound.

Is there some way to make the trait dyn-compatible? Or is it impossible, and should I just stick with my original code if I want to allow dynamic dispatch?


Solution

  • I was able to resolve this by changing the trait method to not take Self, instead defining a new struct to hold the relevant data, and passing that instead.