Hi,
I'm learning Rust, and also trying to progressively move from hacky scripts to acceptable code, as I'm not a developer by trade even though I have experience with programming quick and dirty things in other languages. I've started a small project to experiment with a few concepts.
In this file replicating a part of what I'm doing, I'm creating a concept Notifier which can send_message. It's a trait and there are several implementations. I've added a concept of NotifierChain, which accepts a sort of builder pattern (probably not by the book though) to aggregate several Notifiers. The NotifierChain behaves like a Notifier and can send_message too, which it does by looping over each Notifier it knows about and calling its own send_message method. To make this as general as possible, the NotifierChain therefore implements the Notifier trait. So far so good.
Now I get stuck at the next thing I'd like to improve: rather than creating a NotifierChain and adding Notifier instances to it, I'd like the extra flexibility to create a Notifier, and then chain_with another one to return a NotifierChain. I'm tempted to add chain_with to the Notifier trait, with a default implementation that will work for all my "regular" Notifier structs, and override it inside NotifierChain. However, no matter how I approach this, I get stuck and drown quickly in error messages I'm not sure how to handle. But if I don't, I have to define chain_with with exactly the same definition in each Notifier struct, which sounds like a really bad idea.
What would be a clean solution to this problem? It's not so much that I need this; I'm just as well creating an empty NotifierChain first whenever I need to sequence 2 Notifiers. However I think I might learn something useful if someone manages to explain the solution to me...
Below the code that works as is, with comments as to the changes I'm not successful at making.
Thanks for any inputs on this!
Pierric.
trait Notifier {
fn send_message(&self, msg: String);
// fn chain_with would ideally come in here with a default implementation
// used by "regular" Notifier implementations, and overriden by the NotifierChain one
}
struct NotifA;
impl NotifA {
fn new() -> Self { Self {} }
// for now, all I can manage is to have the same implementation duplicated
// for every implementation of a regular Notifier
fn chain_with(self, other: Box<dyn Notifier>) -> NotifierChain {
let nc = NotifierChain::new().chain_with(Box::new(self));
nc.chain_with(other)
}
}
impl Notifier for NotifA {
fn send_message(&self, msg: String) {
println!("{}", msg);
}
}
struct NotifB;
impl NotifB {
fn new() -> Self { Self {} }
// same code as in NotifA
fn chain_with(self, other: Box<dyn Notifier>) -> NotifierChain {
let nc = NotifierChain::new().chain_with(Box::new(self));
nc.chain_with(other)
}
}
impl Notifier for NotifB {
fn send_message(&self, msg: String) {
println!("{}", msg);
}
}
struct NotifierChain {
notifiers: Vec<Box<dyn Notifier>>
}
impl NotifierChain {
fn new() -> Self {
Self {
notifiers: Vec::new()
}
}
fn chain_with(mut self, other: Box<dyn Notifier>) -> NotifierChain {
self.notifiers.push(other);
self
}
}
impl Notifier for NotifierChain {
fn send_message(&self, msg: String) {
for n in self.notifiers.iter() {
n.send_message(msg.clone());
}
}
}
fn main() {
let a = NotifA::new().chain_with(Box::new(NotifB::new()));
a.send_message("fdfs".to_owned());
}
Output:
fdfs
fdfs
Errors:
Compiling playground v0.0.1 (/playground)
warning: associated function `chain_with` is never used
--> src/main.rs:27:8
|
27 | fn chain_with(self, other: Box<dyn Notifier>) -> NotifierChain {
| ^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: `playground` (bin "playground") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 2.10s
Running `target/debug/playground`