我正在实现命令模式的实现,其中订阅者将从读取器线程接收数据,并且我在使用 Rust 模拟测试我的实现时遇到问题。考虑这个精简的例子:
use std::thread;
pub trait Subscriber: Sync + Send {
fn callback(&self);
}
pub struct Publisher {
thread_handler: Option<thread::JoinHandle<()>>,
}
impl Publisher {
pub fn new() -> Self {
Self {
thread_handler: None,
}
}
pub fn start(&mut self, sub: Box<dyn Subscriber>) {
self.thread_handler = Some(thread::spawn(move || {
sub.callback();
}));
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::*;
mock! {
TestSubscriber {}
impl Subscriber for TestSubscriber {
fn callback(&self);
}
}
#[test]
fn test_subscriber() {
let mut publisher = Publisher::new();
let mut mock_test_subscriber = Box::new(MockTestSubscriber::new());
publisher.start(mock_test_subscriber);
mock_test_subscriber.expect_callback().times(1);
thread::sleep(std::time::Duration::from_millis(10));
}
}
这会抛出
error[E0382]: borrow of moved value: `mock_test_subscriber`
--> src/foo.rs:46:9
|
42 | let mut mock_test_subscriber = Box::new(MockTestSubscriber::new());
| ------------------------ move occurs because `mock_test_subscriber` has type `Box<MockTestSubscriber>`, which does not implement the `Copy` trait
43 |
44 | publisher.start(mock_test_subscriber);
| -------------------- value moved here
45 |
46 | mock_test_subscriber.expect_callback().times(1);
| ^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
我真的不知道如何解决这个问题,我尝试将
dyn Subscriber
包裹在 Arc
周围,但仍然无法使其工作。任何建议将不胜感激。或者也许有更好的设计来达到相同的结果。
您尝试使用
Arc
的方向是正确的 - 它应该是在进行临时多线程时首先尝试的事情。由于您正在尝试在两个线程之间共享 mock_test_subscriber
,因此这正是您所需要的。所以首先我们将 Publisher::start
的签名更改为使用 Arc
:
pub fn start(&mut self, sub: Arc<dyn Subscriber>) {
self.thread_handler = Some(thread::spawn(move || {
sub.callback();
}));
}
...并相应更新调用站点:
let mock_test_subscriber = Arc::new(MockTestSubscriber::new());
// create another reference by cloning the Arc
// then coerce the underlying concrete type into a trait object
publisher.start(Arc::clone(&mock_test_subscriber) as Arc<dyn Subscriber>);
请注意,我删除了
mut
上的 mock_test_subscriber
,因为这可能不是您的意思。写入 let mut foo = Arc::new<()>;
仅允许您将 foo
设置为另一个 Arc
(即指向其他内容),而不修改其基础值。 Arc
在语义上是共享引用,因此由于 Rust 关于可变别名的规则,它不能是可变的。从语法上讲,这表示为 Arc
未实现 DerefMut
。
要获取对基础值的可变引用,请使用
Arc<Mutex<T>>
或 Arc<RwLock<T>>
。