This is a 'sequel' to a previous question of mine, with a more representative example, and a different approach informed by the accepted answer.
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;
}
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?
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.