如何在 Rust 中使用 Arc<Mutex<dyn SomeTrait>> 进行运行时多态性?



假设我想写一个代码,在运行时可以接收共享同一接口的不同类型的解码器,即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_ptrstd::dynamic_pointer_cast<SpecificDecoder1>等。这就是我想要做的。

可以从Arc<Mutex<dyn Trait>>向下转换为Arc<Mutex<Type>>,尽管您将在下面看到它需要一些unsafe,而且它不是一个好的工作流程。在此之前,我鼓励探索其他途径(以及一般的免责声明,下转换通常是一种代码气味,表示定义不清的抽象(。

无论如何,一个关键部分是,如果AB兼容,则从Arc<A>Arc<B>的强制转换是可能的,这意味着它们具有相同的大小、对齐方式,并符合其他标准,就像执行std::mem::transmute一样(并且相同的Drop行为是理想的,但在技术上不需要(。如果满足这些条件,则通过Arc::into_rawArc::from_raw执行此强制转换,指针强制转换介于两者之间。请参阅后者的文档以了解更多信息。

我们还知道,从Mutex<dyn Trait>Mutex<Type>的转变是安全的(如果dyn TraitType(,因为未经尺寸化的矫顽力要求它们具有相同的布局。

另一个关键部分:我们需要从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");
}
}

最新更新