问题描述
任务是为基本特征对象向量过滤掉超特征对象:
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>
,您可以使用逃生舱口 into_raw
和 from_raw
。前者的文档如下:
从原始指针构造一个 Rc。
原始指针必须先前已通过调用 Rc<U>::into_raw
返回,其中 U
必须与 T
具有相同的大小和对齐方式。如果 U
是 T
,则这是微不足道的。请注意,如果 U
不是 T
但具有相同的大小和对齐方式,这基本上类似于转换不同类型的引用。有关在这种情况下适用哪些限制的更多信息,请参阅 mem::transmute
。
from_raw
的用户必须确保 T
的特定值只删除一次。
这个函数是不安全的,因为使用不当可能会导致内存不安全,即使返回的 Rc<T>
从未被访问过。
考虑到这一点,由于我们只能访问 TraitA
,因此它需要一个 as_b()
函数才能将自身作为 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))
}
}
有了我们可以使用的这个函数,使用 .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();
}
}
在 playground 上一起查看。
如果可能,我仍然建议不要这样做。例如,使用上述方法从 &Rc<dyn TraitA>
转到 Option<&dyn TraitB>
是完全安全的,没有所有限制。像这样的东西不会有限制和不安全:
for b in vec_a.iter().filter_map(|a| a.as_b()) {
// ...
}