我对Rust还是很陌生,来自Java背景。我正在尝试在Rust中实现观察者模式(不过,我想这不是惯用的Rust)。我的尝试是这样的:
use crate::observable::{Listener, trigger_listeners};
mod observable {
pub trait Listener {
fn trigger(&mut self);
}
pub fn trigger_listeners(listeners: Vec<&mut Box<dyn Listener>>) {
for mut x in listeners {
x.trigger();
}
}
}
struct Mock {
times_called: u32
}
impl Listener for Mock {
fn trigger(&mut self) {
self.times_called += 1;
}
}
#[test]
fn test() {
let mut mock = Box::new(Mock{ times_called: 0 });
trigger_listeners(vec![&mut mock]);
assert_eq!(mock.times_called, 1)
}
每个侦听器都应实现Listener特性,并作为数组传递给函数trigger_listener
。
这给了我以下错误:
error[E0308]: mismatched types
--> src/lib.rs:27:28
|
27 | trigger_listeners(vec![&mut mock]);
| ^^^^^^^^^ expected trait observable::Listener, found struct `Mock`
|
= note: expected type `&mut std::boxed::Box<(dyn observable::Listener + 'static)>`
found type `&mut std::boxed::Box<Mock>`
error: aborting due to previous error
我当时在想,因为Mock实现了特征监听器,所以我可以将其作为参考传递。
我的另一种尝试是仅使用已移动(pub fn trigger_listeners(listeners: Vec<Box<dyn Listener>>) {}
)的Box。此方法有效,但随后我将无法再访问mock.times_called
。
我也尝试过使用Rc
,但这还是行不通的。
答案 0 :(得分:1)
问题实际上出在您的trigger_listeners函数上,而不是特定的键入本身上。
您将在这里进入泛型领域,值得一读https://doc.rust-lang.org/rust-by-example/generics.html以获得更好的理解,但是开始之前,您需要做的就是修改{{1} }发挥作用。
您当前有
trigger_listeners
在锈病中,特征扮演双重角色,在某种程度上它们也被视为类型。因此,您需要对方法签名进行概括以反映这一点。
pub fn trigger_listeners(listeners: Vec<&mut Box<dyn Listener>>) {
for mut x in listeners {
x.trigger();
}
}
这里我们说的是trigger_listeneres应该接受任何类型
pub fn trigger_listeners<T: Listener>(listeners: Vec<&mut Box<T>>) {
for mut x in listeners {
x.trigger();
}
}
,其中该类型实现了T
特征,而不是在类型签名中传递特征本身。
编辑:
正如trentctl所指出的,并基于您需要非连续类型的vecs才能将其传递给Listener
函数的事实,我们需要做一些修改。
1)实际上,我们可以在没有Box的情况下执行此操作,以简化操作。
trigger_listeners
2)要接受未调整大小的dyn侦听器的vec,我们需要将?Sized特质添加到trigger_listeners函数的特质范围内。
let mut mock = Mock { times_called: 0 };
let mut mock2 = Mock2 { times_called: 0 };
let items: Vec<&mut dyn Listener> = vec![&mut mock, &mut mock2];
trigger_listeners(&mut items);
另一点,如果您不希望传递给trigger_listener函数的类型发生改变,则可以不使用trait对象,而可以将类型包装为枚举类型并传递这些vec而不是特征对象。但是,如果您希望库的用户使用自己实现的Listener trait扩展已知类型,那么trait对象就是解决之道。