-1

I need to create a hashmap that stores references to other objects as the values, with the names of those objects as the keys (HashMap<String, Consumer>).

The overall system is like a mini-version of Apache Kafka for event handling. So Event objects get passed to Topics where they are then pushed to Consumer objects.

Right now I'm having a hard time setting up the subscribe & unsubscribe functions for adding or removing entries in the Topic's hashmap for tracking who is subscribing to it. The compiler is complaining about me 'moving' Consumers to the Topic even though I'm just trying to store/remove a shared immutable reference to the Consumer. I tried using Rc but I still don't have any luck with it. Let me know if you know of a solution.

use std::collections::HashMap;



//TODO: this will likely need to be re-vamped for more diverse event types
pub struct Event {
    etype: String,
    msg: String,
}


// Trait to make things easier across whatever will act as the 'Consumer' of Events from a Topic
pub trait Consume {
    fn consume(event: Event){
        
    }
}

#[derive(PartialEq)]
struct Consumer {
    name : String,
}

impl Consume for Consumer {
    fn consume(event: Event) {
        println!("{}: {}", event.etype, event.msg);
    }
}

pub struct Topic {
    name: String,
    subscribers: HashMap<String, Consumer>,
    events: Vec<Event>,
}

impl Topic {
    fn new(tname: String) -> Topic {
        return Topic {
            name: tname,
            subscribers: HashMap::new(),
            events: Vec::new(),
        };
    }

    //TODO:
    /// Don't know if Box, RefCell, Rc, or what will be needed here to make the compiler happy
    fn subscribe(&mut self, consumer: &Consumer) {
        if self.subscribers.contains_key(&consumer.name) == false {
            let cons_ref = consumer;
            // eprintln!("Does not contain consumer")
            let map = &mut self.subscribers;
            map.entry(consumer.name.clone()).or_insert(consumer);
        }
    }

    fn unsubscribe(&mut self, consumer: Consumer) {
        if self.subscribers.contains_key(&consumer.name) {
            // eprintln!("Does not contain consumer")
            self.subscribers.remove_entry(&consumer.name);
        }
    }
}

Here are the tests for the above code:

#[cfg(test)]
mod tests {

    use super::*;


    // == EVENTS == 
    #[test]
    fn test_create_event(){
        let type_name = "Test";
        let message = "Hello, World!";

        let event = Event {
            etype: type_name.to_string(),
            msg: message.to_string()
        };

        assert_eq!(event.etype, type_name);
        assert_eq!(event.msg, message);
    }

    // == TOPIC == 
    #[test]
    fn test_topic_create(){
        let topic_name = "Test";
        let topic = Topic::new(topic_name.to_string());

        assert_eq!(topic.name, topic_name);
    }

    #[test]
    fn test_topic_subscribe(){
        let topic_name = "Topic";
        let mut topic = Topic::new(topic_name.to_string());

        let consumer_name = "Consumer";
        let cons = Consumer{name: consumer_name.to_string()};

        // Subscribe
        topic.subscribe( cons);

        let test_subscriber = topic.subscribers[consumer_name].name.clone();

        assert_eq!(test_subscriber, consumer_name);
    }

    //TODO: Write this out
    #[test]
    fn test_topic_unsubscribe(){
        let topic_name = "Topic";
        let mut topic = Topic::new(topic_name.to_string());

        let consumer_name = "Consumer";
        let cons = Consumer{name: consumer_name.to_string()};

        topic.subscribe( &cons);

        // Unsubscribe
        topic.unsubscribe(&cons);
    }

    // Add Event to Topic


    // == CONSUMER == 
    // Consume Message

    
}

Here is the output for Cargo Check:

Checking events v0.1.0 (/home/mike/Projects/Rust/Game/events)
warning: unused variable: `event`
  --> src/lib.rs:15:16
   |
15 |     fn consume(event: Event){
   |                ^^^^^ help: if this is intentional, prefix it with an underscore: `_event`
   |
   = note: `#[warn(unused_variables)]` on by default

error[E0507]: cannot move out of `*consumer` which is behind a shared reference
  --> src/lib.rs:50:36
   |
50 |             let cons_ref = Rc::new(*consumer);
   |                                    ^^^^^^^^^ move occurs because `*consumer` has type `Consumer`, which does not implement the `Copy` trait
   |
help: consider cloning the value if the performance cost is acceptable
   |
50 -             let cons_ref = Rc::new(*consumer);
50 +             let cons_ref = Rc::new(consumer.clone());
   |

For more information about this error, try `rustc --explain E0507`.
warning: `events` (lib) generated 1 warning
error: could not compile `events` (lib) due to 1 previous error; 1 warning emitted
3
  • 1
    Please include the full error from cargo check (not your IDE). Commented Aug 19, 2024 at 20:11
  • @ChayimFriedman Ok, just updated the post. Commented Aug 19, 2024 at 20:13
  • 1
    Votes on StackOverflow have nothing to do with whether people "like" or "dislike" the post. I suggest you have a look through the site's help center to understand more. Commented Aug 20, 2024 at 5:14

2 Answers 2

0

I changed up the code to take in Rc as the parameter type and updated the test code to clone the Rc when calling the 'subscribe' function. Everything checks out so far.

use std::collections::HashMap;
use std::rc::Rc;



//TODO: this will likely need to be re-vamped for more diverse event types
pub struct Event {
    etype: String,
    msg: String,
}


// Trait to make things easier across whatever will act as the 'Consumer' of Events from a Topic
pub trait Consume {
    fn consume(event: Event){
        
    }
}

#[derive(PartialEq, Clone)]
struct Consumer {
    name : String,
}

impl Consume for Consumer {
    fn consume(event: Event) {
        println!("{}: {}", event.etype, event.msg);
    }
}

pub struct Topic {
    name: String,
    subscribers: HashMap<String, Rc<Consumer>>,
    events: Vec<Event>,
}

impl Topic {
    fn new(tname: String) -> Topic {
        return Topic {
            name: tname,
            subscribers: HashMap::new(),
            events: Vec::new(),
        };
    }

    //TODO:
    /// Don't know if Box, RefCell, Rc, or what will be needed here to make the compiler happy
    fn subscribe(&mut self, consumer: Rc<Consumer>) {
        if self.subscribers.contains_key(&consumer.name) == false {
            // let cons_ref = Rc::new(consumer);

            let cons_name  = consumer.name.clone();
            // eprintln!("Does not contain consumer")
            let map = &mut self.subscribers;
            map.entry(cons_name).or_insert(consumer);
        }
    }

    fn unsubscribe(&mut self, consumer: Rc<Consumer>) {
        let cons_name = consumer.name.clone();
        if self.subscribers.contains_key(&cons_name) {
            // eprintln!("Does not contain consumer")
            self.subscribers.remove_entry(&cons_name);
        }
    }
}

Test Code:

#[cfg(test)]
mod tests {

    use super::*;


    // == EVENTS == 
    #[test]
    fn test_create_event(){
        let type_name = "Test";
        let message = "Hello, World!";

        let event = Event {
            etype: type_name.to_string(),
            msg: message.to_string()
        };

        assert_eq!(event.etype, type_name);
        assert_eq!(event.msg, message);
    }

    // == TOPIC == 
    #[test]
    fn test_topic_create(){
        let topic_name = "Test";
        let topic = Topic::new(topic_name.to_string());

        assert_eq!(topic.name, topic_name);
    }

    #[test]
    fn test_topic_subscribe(){
        let topic_name = "Topic";
        let mut topic = Topic::new(topic_name.to_string());

        let consumer_name = "Consumer";
        let cons = Consumer{name: consumer_name.to_string()};
        let cons_ref = Rc::new(cons);

        // Subscribe
        topic.subscribe( cons_ref);

        let test_subscriber = topic.subscribers[consumer_name].name.clone();

        assert_eq!(test_subscriber, consumer_name);
    }

    //TODO: Write this out
    #[test]
    fn test_topic_unsubscribe(){
        let topic_name = "Topic";
        let mut topic = Topic::new(topic_name.to_string());

        let consumer_name = "Consumer";
        let cons = Consumer{name: consumer_name.to_string()};
        let cons_ref = Rc::new(cons);

        topic.subscribe( cons_ref.clone());

        // Unsubscribe
        topic.unsubscribe(cons_ref);
    }

    // Add Event to Topic


    // == CONSUMER == 
    // Consume Message

    
}

Still have to write the assert for the unsubscribe method but everything else is fine!

Sign up to request clarification or add additional context in comments.

Comments

0

I don't know your final goal, if you want to make a pubsub model with some threading/async in the mix, it is better to use some library, e.g.tokio's boardcast.

But in the case that you just want to practice some rust,

Before thinking about what data structure to use, you need to think about the actual usage.

I notice that in the Consume trait, the consume function doesn't take &self/&mut self as argument.

fn consume(event: Event) {
        println!("{}: {}", event.etype, event.msg);
    }
}

does that mean you want the consume function be a static function (independent of data in Consumer e.g. Consumer.data)?

are you expect multi struct with trait of Consume?

I'll show you how I iterate here

definition of event

#[derive(Debug, Clone)]
pub struct Event {
    pub etype: String,
    pub msg: String,
}

Iteration 1

only one data type is consumer, thus no need of a trait of Consume.

the consume function only take event:Event argument, static to the data structure.

it doesn't really serve any purporse but anyways.

mod iteration1 {
    use crate::*;

    /// Only one type Consumer type
    #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
    pub struct Consumer {
        name: String,
    }

    /// if there are only one data type of consumer,
    /// it doesn't need a trait
    impl Consumer {
        /// It is a static function
        /// that doesn't take &self or &mut self as argument
        pub fn consume(event: Event) {
            println!("consuming : {} - {}", event.etype, event.msg);
        }
    }

    pub struct Topic {
        pub name: String,
        pub subscribers: BTreeSet<Consumer>,
        pub events: Vec<Event>,
    }

    impl Topic {
        pub fn subscribe(&mut self, consumer: Consumer) {
            let _ = self.subscribers.insert(consumer);
        }
        pub fn unsubscribe(&mut self, consumer: Consumer) {
            let _ = self.subscribers.remove(&consumer);
        }
    }
}

Iteration 2

Now the consume function take &mut self as argument.

mod mod2 {
    use crate::*;

    /// Still only one type Consumer type
    #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
    pub struct Consumer {
        name: String,
    }

    /// if there are only one data type of consumer,
    /// it doesn't need a trait
    impl Consumer {
        /// It take &mut self (and by extend &self) as a argument
        pub fn consume(&mut self, event: Event) {
            println!("{} consuming : {} - {}", self.name, event.etype, event.msg);
        }
    }

    pub struct Topic {
        pub name: String,
        pub subscribers: BTreeSet<Consumer>,
        pub events: Vec<Event>,
    }

    impl Topic {
        pub fn subscribe(&mut self, consumer: Consumer) {
            let _ = self.subscribers.insert(consumer);
        }
        pub fn unsubscribe(&mut self, consumer: Consumer) {
            let _ = self.subscribers.remove(&consumer);
        }
    }
}

Iteration 3

Now we add a different type of consumer

finally need to introduce the Consume trait. use key() to bind the identifying key with the data structure, for ease of management.

Problem

we cannot borrow as mutable, since RC does not allow mutable borrow.

mod mod3 {

    use std::collections::BTreeMap;

    use crate::*;

    /// for more than one type of consumer
    pub trait Consume {
        fn consume(&self, event: Event);
        fn consume_mut(&mut self, event: Event);
        /// key for inserting into the BTreeMap
        fn key(&self) -> String;
    }

    #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
    pub struct Consumer {
        name: String,
    }

    impl Consume for Consumer {
        fn consume(&self, event: Event) {
            println!("consuming : {} - {}", event.etype, event.msg);
        }
        fn consume_mut(&mut self, event: Event) {
            println!("consuming : {} - {}", event.etype, event.msg);
        }
        fn key(&self) -> String {
            format!("Consumer {}", self.name)
        }
    }

    #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
    pub struct ConsumerPuppy {
        name: String,
    }

    impl Consume for ConsumerPuppy {
        fn consume(&self, event: Event) {
            println!("woff, consuming : {} - {}", event.etype, event.msg);
        }
        fn consume_mut(&mut self, event: Event) {
            println!("woff, consuming : {} - {}", event.etype, event.msg);
        }
        fn key(&self) -> String {
            format!("Consumer {} Woff", self.name)
        }
    }

    pub struct Topic {
        pub name: String,
        pub subscribers: BTreeMap<String, Rc<dyn Consume>>,
        pub events: Vec<Event>,
    }

    impl Topic {
        pub fn subscribe(&mut self, consumer: Rc<dyn Consume>) {
            let _ = self.subscribers.insert(consumer.key(), consumer);
        }
        pub fn unsubscribe(&mut self, consumer: Rc<dyn Consume>) {
            let _ = self.subscribers.remove(&consumer.key());
        }
        pub fn publish(&mut self, event: Event){
            for (_,v) in self.subscribers.iter_mut(){
                v.consume(event.clone());

                //  v.consume_mut(event.clone());
                //  ^
                //  this line will have error, since you cannot borrow Rc as mut 
            }
        }
    }
}

Iteration 4

we need to use Rc<RefCell<_>> for internal mutablility,

however we cannot deference Rc<RefCell<dyn trait>> to &dyn trait,

thus, we need to dereference to Box<dyn trait>

thus,

Rc< RefCell < Box< dyn trait> > > 
|   |         |         L The trait we use
|   |         L allow deference into `&dyn trait`
|   L internal mutability
L cloneable
mod iteration4 {

    use std::collections::BTreeMap;
    use std::cell::RefCell;

    use crate::*;

    /// for more than one type of consumer
    pub trait Consume {
        fn consume(&self, event: Event);
        fn consume_mut(&mut self, event: Event);
        /// key for inserting into the BTreeMap
        fn key(&self) -> String;
    }

    #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
    pub struct Consumer {
        name: String,
    }

    impl Consume for Consumer {
        fn consume(&self, event: Event) {
            println!("consuming : {} - {}", event.etype, event.msg);
        }
        fn consume_mut(&mut self, event: Event) {
            println!("consuming : {} - {}", event.etype, event.msg);
        }
        fn key(&self) -> String {
            format!("Consumer {}", self.name)
        }
    }

    #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
    pub struct ConsumerPuppy {
        name: String,
    }

    impl Consume for ConsumerPuppy {
        fn consume(&self, event: Event) {
            println!("woff, consuming : {} - {}", event.etype, event.msg);
        }
        fn consume_mut(&mut self, event: Event) {
            println!("woff, consuming : {} - {}", event.etype, event.msg);
        }
        fn key(&self) -> String {
            format!("Consumer {} Woff", self.name)
        }
    }

    pub struct Topic {
        pub name: String,
        pub subscribers: BTreeMap<String, Rc<RefCell<Box<dyn Consume>>>>,
        pub events: Vec<Event>,
    }

    

    impl Topic {
        pub fn subscribe(&mut self, consumer: Rc<RefCell<Box<dyn Consume>>>) {
            let key = consumer.as_ref().borrow().key();
            let _ = self.subscribers.insert(key, consumer);
        }
        pub fn unsubscribe(&mut self, consumer: Rc<RefCell<Box<dyn Consume>>>) {
            let key = consumer.as_ref().borrow().key();
            let _ = self.subscribers.remove(&key);
        }
        pub fn publish(&mut self, event: Event){
            for (_,v) in self.subscribers.iter_mut(){
                v.as_ref().borrow().consume(event.clone());

                v.as_ref().borrow_mut().consume_mut(event.clone());
            }
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.