簡體   English   中英

將可變特征對象的向量傳遞給函數

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM