假设我想写一个代码,在运行时可以接收共享同一接口的不同类型的解码器,即trait Decoder
。我想得到Arc<Mutex<dyn Decoder>>
并向下转换到我的特定解码器。类似这样的东西:
use std::sync::{Arc, Mutex};
trait Decoder {}
struct SpecificDecoder1;
impl Decoder for SpecificDecoder1 {}
struct SpecificDecoder2;
impl Decoder for SpecificDecoder2 {}
fn main() {
let decoder: Arc<Mutex<dyn Decoder>> = Arc::new(Mutex::new(SpecificDecoder1));
if let Ok(specific_decoder_1) = decoder.downcast::<Mutex<SpecificDecoder1>>() {
} else if let Ok(specific_decoder_2) = decoder.downcast::<Mutex<SpecificDecoder2>>() {
} else {
}
}
游乐场
错误:
error[E0599]: no method named `downcast` found for struct `Arc<Mutex<dyn Decoder>>` in the current scope
--> src/main.rs:26:45
|
26 | if let Ok(specific_decoder_1) = decoder.downcast::<Mutex<SpecificDecoder1>>() {
| ^^^^^^^^ method not found in `Arc<Mutex<dyn Decoder>>`
error[E0599]: no method named `downcast` found for struct `Arc<Mutex<dyn Decoder>>` in the current scope
--> src/main.rs:28:52
|
28 | } else if let Ok(specific_decoder_2) = decoder.downcast::<Mutex<SpecificDecoder2>>() {
| ^^^^^^^^ method not found in `Arc<Mutex<dyn Decoder>>`
但downcast
只有这个实现:
pub fn downcast<T>(self) -> Result<Arc<T>, Arc<dyn Any + 'static + Send + Sync>>
其中T: 任意+发送+同步+'静态,
看起来CCD_ 4必须实现CCD_。我想我必须做这样的事情:
impl Any for SpecificDecoder1 {
fn type_id(&self) -> TypeId {
//what to do here? TypeId has no constructors
}
}
另外,这是正确的方式吗?在C++上,我会使用std::shared_ptr
和std::dynamic_pointer_cast<SpecificDecoder1>
等。这就是我想要做的。
可以从Arc<Mutex<dyn Trait>>
向下转换为Arc<Mutex<Type>>
,尽管您将在下面看到它需要一些unsafe
,而且它不是一个好的工作流程。在此之前,我鼓励探索其他途径(以及一般的免责声明,下转换通常是一种代码气味,表示定义不清的抽象(。
无论如何,一个关键部分是,如果A
和B
兼容,则从Arc<A>
到Arc<B>
的强制转换是可能的,这意味着它们具有相同的大小、对齐方式,并符合其他标准,就像执行std::mem::transmute
一样(并且相同的Drop
行为是理想的,但在技术上不需要(。如果满足这些条件,则通过Arc::into_raw
和Arc::from_raw
执行此强制转换,指针强制转换介于两者之间。请参阅后者的文档以了解更多信息。
我们还知道,从Mutex<dyn Trait>
到Mutex<Type>
的转变是安全的(如果dyn Trait
是Type
(,因为未经尺寸化的矫顽力要求它们具有相同的布局。
另一个关键部分:我们需要从Any
特性扩展,因为它提供了一种type_id()
方法来在dyn Decoder
后面获得当前类型。
有了这些,我们可以这样做:
use std::any::{Any, TypeId};
use std::sync::{Arc, Mutex};
trait Decoder: Any {}
struct SpecificDecoder1;
struct SpecificDecoder2;
impl Decoder for SpecificDecoder1 {}
impl Decoder for SpecificDecoder2 {}
fn downcast<T: Decoder>(decoder: &Arc<Mutex<dyn Decoder>>) -> Option<Arc<Mutex<T>>> {
if (*decoder.lock().unwrap()).type_id() == TypeId::of::<T>() {
let raw: *const Mutex<dyn Decoder> = Arc::into_raw(decoder.clone());
let raw: *const Mutex<T> = raw.cast();
// SAFETY: This is safe because the pointer orignally came from an Arc
// with the same size and alignment since we've checked (via Any) that
// the object within is the type being casted to.
Some(unsafe { Arc::from_raw(raw) })
} else {
None
}
}
fn main() {
let decoder: Arc<Mutex<dyn Decoder>> = Arc::new(Mutex::new(SpecificDecoder1));
if let Some(_specific_decoder_1) = downcast::<SpecificDecoder1>(&decoder) {
println!("decoder1")
} else if let Some(_specific_decoder_2) = downcast::<SpecificDecoder2>(&decoder) {
println!("decoder2")
} else {
println!("neither")
}
}
decoder1
这仍然不理想,因为您必须访问Mutex
才能查看它包含的内容,但这更像是对什么是可能的演示。
我认为您假设Arc<dyn Decoder>
(或Arc<Mutex<dyn Decoder>>
(和Arc<dyn Any>
之间存在继承关系。我不认为这个假设是正确的——trait对象不会继承。以下线路工作:
fn main() {
let decoder: Arc<dyn Any + Send + Sync> = Arc::new(Mutex::new(SpecificDecoder1));
if let Ok(specific_decoder_1) = decoder.clone().downcast::<Mutex<SpecificDecoder1>>() {
println!("specific 1");
} else if let Ok(specific_decoder_2) = decoder.clone().downcast::<Mutex<SpecificDecoder2>>() {
println!("specific 2");
} else {
println!("else");
}
}