Rust beginner here. I have a number of algorithms that are almost identical but, at the final step, they all aggregate the results in slightly differently ways. Let's say the Algorithm
does the following:
pub struct Algorithm<T> {
result_aggregator: Box<dyn ResultAggregator<T>>,
}
impl<T> Algorithm<T> {
pub fn calculate(&self, num1: i32, num2: i32) -> T {
let temp = num1 + num2;
self.result_aggregator.create(temp)
}
}
With this, I can create a few different result aggregator classes to take my temp result and transform it into my final result:
pub trait ResultAggregator<T> {
fn create(&self, num: i32) -> T;
}
pub struct FloatAggregator;
pub struct StringAggregator;
impl ResultAggregator<f32> for FloatAggregator {
fn create(&self, num: i32) -> f32 {
num as f32 * 3.14159
}
}
impl ResultAggregator<String> for StringAggregator {
fn create(&self, num: i32) -> String {
format!("~~{num}~~")
}
}
...and call it like so:
fn main() {
// Here's a float example
let aggregator = FloatAggregator;
let algorithm = Algorithm {
result_aggregator: Box::new(aggregator),
};
let result = algorithm.calculate(4, 5);
println!("The result has value {result}");
// Here's a string example
let aggregator = StringAggregator;
let algorithm = Algorithm {
result_aggregator: Box::new(aggregator),
};
let result = algorithm.calculate(4, 5);
println!("The result has value {result}");
}
This is what I've come up with.
Question: Is it possible to do this without the dynamic box? It's performance critical and I understand that generics are usually a good solution but I've had no luck figuring out how to get it working without dynamic dispatch.
So what's the Rusty solution to this problem? I feel like I'm approaching it with my C# hat on which is probably not the way to go.
You can use an associated type instead of a generic parameter:
pub trait ResultAggregator {
type Output;
fn create(&self, num: i32) -> Self::Output;
}
pub struct FloatAggregator;
pub struct StringAggregator;
impl ResultAggregator for FloatAggregator {
type Output = f32;
fn create(&self, num: i32) -> f32 {
num as f32 * 3.14159
}
}
impl ResultAggregator for StringAggregator {
type Output = String;
fn create(&self, num: i32) -> String {
format!("~~{num}~~")
}
}
pub struct Algorithm<Aggregator> {
result_aggregator: Aggregator,
}
impl<Aggregator: ResultAggregator> Algorithm<Aggregator> {
pub fn calculate(&self, num1: i32, num2: i32) -> Aggregator::Output {
let temp = num1 + num2;
self.result_aggregator.create(temp)
}
}