簡體   English   中英

鑄造Rc <ConcreteType> 到一個Rc <Trait>

[英]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可以保留底層具體類型的信息,並且可以為HorseAnimal實現調用正確的方法(如果有的話;在你的例子中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.

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