簡體   English   中英

如何過濾 Rust 中特定子特征的 RCed 特征對象向量?

[英]How to filter RCed trait objects vector of specific subtrait in Rust?

任務是過濾掉基本特征 object 向量的超特征對象:

use std::rc::Rc;
use std::any::Any;

pub trait TraitA {
    fn get_title(&self) -> &str;
    fn as_any(&self) -> Any;
}

pub trait TraitB: TraitA {
    fn get_something(&self) -> &str;
}

pub fn filter_b(input: Vec<Rc<dyn TraitA>>) -> Vec<Rc<dyn TraitB>> {
    // bs.filter(|it| /* How to do it? */).collect();
}

甚至可能嗎? 有什么線索或建議嗎?

我知道as_any()可以用於向下轉換,但我不確定它是如何與Rc一起工作的,因為它需要所有權(因此需要實例)。

我最初期望答案是“絕對不是”,如果您不知道具體類型, Any都無濟於事。 但事實證明你可以......有警告,我不能 100% 確定它完全安全。

Rc<T>Rc<U>到 go ,您可以使用逃生艙into_rawfrom_raw 前者的文檔如下:

從原始指針構造一個 Rc。

原始指針必須先前已通過調用Rc<U>::into_raw ,其中U必須具有與T相同的大小和 alignment 。 如果UT ,這是微不足道的。 注意,如果U不是T但大小和 alignment 相同,這基本上就像轉換不同類型的引用。 有關在這種情況下適用哪些限制的更多信息,請參閱mem::transmute

from_raw的用戶必須確保只刪除一次特定的T值。

這個 function 是不安全的,因為使用不當可能會導致 memory 不安全,即使返回的Rc<T>從未被訪問過。

考慮到這一點,由於我們只能訪問TraitA ,因此它需要一個as_b() function 才能將自己作為一個TraitB 目標是一個超級特質這一事實並沒有真正的幫助。 然后我們可以像這樣寫一個交叉廣播crosscast

use std::rc::Rc;

trait TraitA {
    fn print_a(&self);

    // SAFETY: the resulting `dyn TraitB` must have the *exact* same address,
    // size, alignment, and drop implementation for `crosscast` to work safely.
    // Basically it must be `self` or maybe a transparently wrapped object.
    unsafe fn as_b(&self) -> Option<&(dyn TraitB + 'static)>;
}

trait TraitB {
    fn print_b(&self);
}

fn crosscast(a: Rc<dyn TraitA>) -> Option<Rc<dyn TraitB>> {
    unsafe {
        let b_ptr = a.as_b()? as *const dyn TraitB;
        let a_ptr = Rc::into_raw(a);
        
        // sanity check
        assert!(a_ptr as *const () == b_ptr as *const ());
        
        Some(Rc::from_raw(b_ptr))
    }
}

有了這個 function 供我們使用,您的問題通過使用.filter_map()變得微不足道:

struct Both {
    data: String,
}

impl TraitA for Both {
    fn print_a(&self) { println!("A: {}", self.data); }
    unsafe fn as_b(&self) -> Option<&(dyn TraitB + 'static)> { Some(self) }
}

impl TraitB for Both {
    fn print_b(&self) { println!("B: {}", self.data); }
}

struct OnlyA {
    data: String,
}

impl TraitA for OnlyA {
    fn print_a(&self) { println!("A: {}", self.data); }
    unsafe fn as_b(&self) -> Option<&(dyn TraitB + 'static)> { None }
}

fn main() {
    let vec_a = vec![
        Rc::new(Both{ data: "both".to_owned() }) as Rc<dyn TraitA>,
        Rc::new(OnlyA{ data: "only a".to_owned() })
    ];

    for a in &vec_a {
        a.print_a();
    }
    
    println!();
    
    let vec_b = vec_a
        .into_iter()
        .filter_map(crosscast)
        .collect::<Vec<_>>();

    for b in &vec_b {
        b.print_b();
    }
}

操場上一起觀看。

如果可能的話,我仍然建議不要這樣做。 例如,使用上述方法從&Rc<dyn TraitA>Option<&dyn TraitB>的 go 將是完全安全的,沒有任何限制。 這樣的事情不會有限制和不安全:

for b in vec_a.iter().filter_map(|a| a.as_b()) {
   // ...
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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