[英]Pass vector of mutable trait objects to function
我對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)
}
每個偵聽器都應實現偵聽器特性,並作為數組傳遞給函數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實現了特征監聽器,所以我可以將其作為參考傳遞。
我的另一種嘗試是僅使用已移動的Box( pub fn trigger_listeners(listeners: Vec<Box<dyn Listener>>) {}
)。 這行得通,但是后來我再也無法訪問mock.times_called
了。
我也嘗試過使用Rc
,但這也沒有用。
實際上,問題出在您的trigger_listeners函數上,而不是特定的鍵入本身上。
您從這里開始進入泛型領域,值得一讀https://doc.rust-lang.org/rust-by-example/generics.html以獲得更好的理解,但請您入門要做的是稍微修改一下一下trigger_listeners
函數的簽名。
您目前有
pub fn trigger_listeners(listeners: Vec<&mut Box<dyn Listener>>) {
for mut x in listeners {
x.trigger();
}
}
在銹病中,特征扮演雙重角色,在某種程度上它們也被視為類型。 因此,您需要對方法簽名進行概括以反映這一點。
pub fn trigger_listeners<T: Listener>(listeners: Vec<&mut Box<T>>) {
for mut x in listeners {
x.trigger();
}
}
這里我們說的是,trigger_listeneres應該接受任何類型T
,其中該類型實現Listener
特性,而不是在類型簽名中傳遞特性本身。
編輯:
正如trentctl所指出的,並基於您需要非連續類型的vecs才能將其傳遞給trigger_listeners
函數的事實,我們需要做一些修改。
1)我們實際上可以在沒有Box的情況下執行此操作,以簡化操作。
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);
2)要接受未調整大小的dyn偵聽器的vec,我們需要將?Sized特質添加到trigger_listeners函數的特質范圍。
pub fn trigger_listeners<t>(listeners: &mut Vec<&mut T>)
where T: Listener + ?Sized
{
for x in listeners {
x.trigger();
}
}
還有一點,如果您不希望傳遞給trigger_listener函數的類型發生更改,則可以放棄使用trait對象,而將您的類型包裝為枚舉類型,並傳遞一個vec而不是trait對象。 但是,如果您期望庫的用戶使用自己實現的Listener trait擴展已知類型,那么trait對象就是解決之道。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.