我需要创建一个哈希图,将对其他对象的引用存储为值,这些对象的名称作为键(HashMap
整个系统就像一个用于事件处理的迷你版 Apache Kafka。因此,Event 对象被传递到 Topics,然后被推送到 Consumer 对象。
现在我很难设置订阅和取消订阅功能来添加或删除主题哈希图中的条目以跟踪谁正在订阅它。编译器抱怨我将消费者“移动”到主题,即使我只是试图存储/删除对消费者的共享不可变引用。我尝试使用 Rc 但仍然没有任何运气。如果您知道解决方案,请告诉我。
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);
}
}
}
以下是对上述代码的测试:
#[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
}
这是货物检查的输出:
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
我更改了代码以采用 Rc 作为参数类型,并更新了测试代码以在调用“订阅”函数时克隆 Rc。到目前为止一切都已检查完毕。
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);
}
}
}
测试代码:
#[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
}
仍然需要为取消订阅方法编写断言,但其他一切都很好!