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

标签 rust

假设我想编写一段代码,在运行时可以接收共享相同接口(interface)的不同类型的解码器,即 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:任意 + 发送 + 同步 + '静态,

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

最佳答案

可以Arc<Mutex<dyn Trait>>向下转型进入Arc<Mutex<Type>>虽然你会在下面看到它需要一些 unsafe这不是一个很好的工作流程。我鼓励在此之前探索其他途径(并且一般免责声明,向下转型通常是一种代码味道,表明抽象定义不明确)。

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

我们还知道从 Mutex<dyn Trait> 转变而来至Mutex<Type>安全(如果 dyn TraitType ),因为 unsized coercions它们必须具有相同的布局。

另一个关键部分:我们需要从 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看看它包含什么,但这更多的是展示什么是可能的。

关于rust - 如何在 Rust 中使用 Arc<Mutex<dyn SomeTrait>> 进行运行时多态性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68173030/

相关文章:

pattern-matching - 从 Options 和 Results 中移除 unwrap 调用而不引入双重缩进的匹配语句

syntax - { .. } 在模式中意味着什么?

lambda - 在结构中存储 lambda 返回迭代器

rust - 如何从特征对象中获取对具体类型的引用?

string - 为什么我不能将 Lines 迭代器收集到一个字符串向量中?

server - 如何在 hyper 中提供静态文件/目录?

validation - 如何创建类型安全的范围受限数字类型?

string - 如何将两个字符串字段与另一个字符串连接起来?

rust - 如何从函数返回 Any?

struct - 为具有多个参数的矩阵实现索引运算符