dyn是trait對象類型的前綴
dyn關鍵字用於強調相關trait的方法是動態分配的。要以這種方式使用trait,它必須是“對象安全”的。
與泛型參數或植入型特質不同,編譯器不知道被傳遞的具體類型。也就是說,該類型已經被抹去。因此,一個dyn Trait引用包含兩個指針。一個指針指向數據(例如,一個結構的實例)。另一個指針指向方法調用名稱與函數指針的映射(被稱為虛擬方法表各vtable)。
impl trait 和 dyn trait 在Rust分別被稱為靜態分發和動態分發,即當代碼涉及多態時,需要某種機制決定實際調動類型。
使用dyn返回trait
Rust編譯器需要知道每個函數的返回類型需要多少空間。這意味着所有函數都必須返回一個具體類型。我們通過以下示例1來展示:
文件名: src/lib.rs
pub trait Animal { fn noise(&self); } pub struct Sheep {} pub struct Cow {} impl Animal for Sheep { fn noise(&self){ println!("mamamama!"); } } impl Animal for Cow { fn noise(&self) { println!("moooooo!"); } }
示例1有trait Animal,我們不能編寫返回Animal的函數,因為其不同的實將需要不同的內存量。
但是,有一個簡單的解決方法。相比直接返回一個trait對象,我們的函數返回一個包含一些Animal的Box。box只是對堆中某些內存的引用。因為引用的大小是靜態已知的,並且編譯器可以保證引用指向已分配的堆Animal,所以我們可以從函數中返回trait。
每當在堆上分配內存時,Rust都會嘗試盡可能明確。因此,如果你的函數以這種方式返回指向堆的trait指針,則需要使用dyn關鍵字編寫返回類型,如示例2:
文件名:src/main.rs
fn random_animal(random_number: f64) -> Box<dyn Animal> { if random_number < 0.5 { Box::new(Sheep {}) } else { Box::new(Cow {}) } }
let random_number = 0.234; let animal = random_animal(random_number); animal.noise();