[英]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_raw
和from_raw
。 前者的文檔如下:
從原始指針構造一個 Rc。
原始指針必須先前已通過調用
Rc<U>::into_raw
,其中U
必須具有與T
相同的大小和 alignment 。 如果U
是T
,這是微不足道的。 注意,如果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.