Strategy Pattern
โ Rust ๐ 2025-12-18 ๐ค surdeus ๐๏ธ 4Hello Rustaceans,
I'm currently investigating how the Strategy Pattern can be implemented in Rust. I've always been interested in reducing branches in my program's processing pipeline.
To illustrate why this frustrates me, here's an analogy:
When you go to the supermarket, you choose either a shopping cart or a bag. Then, as you pick up items in the store, you just put them in your container. You choose your container type once. What you don't do is pick up an item, check what type of container you have, and then put it inโfor every single item. You already know what container you have. You don't need to check on every item; you just put it in.
Yet somehow, when it comes to programming, this pattern is never intuitive.
What follows is an example as to how I would implement the Strategy Pattern using function pointers and how I would it with simple branches during my process pipeline. However this has a fatal flaw as using function pointers wont allow the compiler for function inlining.
Are there any fans of the Strategy Pattern in Rust and do you have any suggestions how I can implement it without losing any compiler optimizations? And btw I am not a huge fan of generics and I am also fairly new to Rust programming.
pub struct StrategyOutput {
pub text: String,
pub score: f32,
pub array: Option<Vec<f32>>,
}
pub struct StrategyExecuter {
config: String,
execute: fn(&StrategyExecuter, u32) -> StrategyOutput,
}
impl StrategyExecuter {
pub fn new(use_strategy_a: bool, config: String) -> Self {
Self {
config,
execute: if use_strategy_a {
Self::strategy_a
} else {
Self::strategy_b
},
}
}
pub fn process(&self, amount: u32) -> StrategyOutput {
(self.execute)(self, amount)
}
fn strategy_a(&self, amount: u32) -> StrategyOutput {
StrategyOutput {
text: format!("StrategyA: {}", self.config),
score: amount as f32,
array: Some((0..amount).map(|i| i as f32).collect()),
}
}
fn strategy_b(&self, amount: u32) -> StrategyOutput {
StrategyOutput {
text: format!("StrategyB: {}", self.config),
score: amount as f32,
array: None,
}
}
}
pub struct NormalExecuter {
config: String,
}
impl NormalExecuter {
pub fn new(config: String) -> Self {
Self { config }
}
pub fn process(&self, amount: u32, use_strategy_a: bool) -> StrategyOutput {
let array = if use_strategy_a {
Some((0..amount).map(|i| i as f32).collect())
} else {
None
};
StrategyOutput {
text: format!("StrategyA: {}", self.config),
score: amount as f32,
array: array,
}
}
}
3 posts - 3 participants
๐ท๏ธ Rust_feed