[英]Casting Rc<ConcreteType> to an Rc<Trait>
Horse
是一個實現Animal
特性的結構。 我有一個Rc<Horse>
和一個需要接受Rc<Animal>
的函數,所以我想從Rc<Horse>
轉換為Rc<Animal>
。
我這樣做了:
use std::rc::Rc;
struct Horse;
trait Animal {}
impl Animal for Horse {}
fn main() {
let horse = Rc::new(Horse);
let animal = unsafe {
// Consume the Rc<Horse>
let ptr = Rc::into_raw(horse);
// Now it's an Rc<Animal> pointing to the same data!
Rc::<Animal>::from_raw(ptr)
};
}
這是一個好的解決方案嗎? 這是正確的嗎?
Boiethios的答案已經解釋了可以使用as
或者甚至隱含地在某些情況下明確地執行向上轉換。 我想補充一些關於機制的更多細節。
我將首先解釋為什么您的不安全代碼正常工作。
let animal = unsafe {
let ptr = Rc::into_raw(horse);
Rc::<Animal>::from_raw(ptr)
};
unsafe
塊中的第一行消耗horse
並返回*const Horse
,它是指向具體類型的指針。 指針正是你所期望的 - horse
的數據的內存地址(忽略了在你的例子中Horse
是零大小且沒有數據的事實)。 在第二行中,我們調用Rc::from_raw()
; 讓我們看一下該函數的原型:
pub unsafe fn from_raw(ptr: *const T) -> Rc<T>
由於我們為Rc::<Animal>
調用此函數,因此預期的參數類型為*const Animal
。 然而我們的ptr
有類型*const Horse
,為什么編譯器會接受代碼? 答案是編譯器執行unsized強制 ,這是一種在某些類型的某些地方執行的隱式強制轉換 。 具體來說,我們將指向具體類型的指針轉換為指向實現Animal
特征的任何類型的指針。 由於我們不知道確切的類型,現在指針不再僅僅是一個內存地址 - 它是一個內存地址以及對象的實際類型的標識符,即所謂的胖指針 。 這樣,從胖指針創建的Rc
可以保留底層具體類型的信息,並且可以為Horse
的Animal
實現調用正確的方法(如果有的話;在你的例子中Animal
沒有任何函數,但當然,如果有的話,這應該繼續有效。
我們可以通過打印它們的大小來看到兩種指針之間的區別
let ptr = Rc::into_raw(horse);
println!("{}", std::mem::size_of_val(&ptr));
let ptr: *const Animal = ptr;
println!("{}", std::mem::size_of_val(&ptr));
這段代碼首先使ptr
a *const Horse
,打印指針的大小,然后使用unsized強制將ptr
轉換為和*const Animal
並再次打印其大小。 在64位系統上,將打印
8
16
第一個是簡單的內存地址,第二個是內存地址以及有關指針的具體類型的信息。 (具體來說,胖指針包含指向虛方法表的指針。)
現在讓我們看一下Boethios答案中代碼中發生的事情
let animal = horse as Rc<Animal>;
或者等價的
let animal: Rc<Animal> = horse;
也執行未經證實的強制。 編譯器如何知道如何為Rc
而不是原始指針執行此操作? 答案是CoerceUnsized
特性專門用於此目的 。 您可以閱讀關於動態大小類型的強制轉換的RFC以獲取更多詳細信息。
我認為你的解決方案是正確的,而我不是不安全代碼的專家。 但是,您不必使用不安全的代碼來執行向上轉換等簡單的操作:
use std::rc::Rc;
trait Animal {}
struct Horse;
impl Animal for Horse {}
fn main() {
let horse = Rc::new(Horse);
let animal = horse as Rc<Animal>;
}
如果你想將它傳遞給一個函數,你甚至不需要強制轉換:
fn gimme_an_animal(_animal: Rc<Animal>) {}
fn main() {
let horse = Rc::new(Horse);
gimme_an_animal(horse);
}
因為Horse
實現了Animal
,所以馬是動物。 你不需要做任何特殊的鑄造它。 請注意,此轉換具有破壞性,您無法從Rc<Animal>
創建Rc<Horse>
Rc<Animal>
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.