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

jaql4c8m  于 2023-10-20  发布在  其他
关注(0)|答案(2)|浏览(171)

假设我想写一段代码,在运行时可以接收共享相同接口的不同类型的解码器,即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 {
    }
}

Playground
错误:

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:Any + Send + Sync + 'static,
T必须实现Any。我想我必须做这样的事情:

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>等等。这就是我想做的。

tvokkenx

tvokkenx1#

可以从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 trait,因为它提供了一个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才能看到它包含的内容,但这更多的是一个可能性的演示。

h79rfbju

h79rfbju2#

我认为你假设了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");
    }
}

相关问题