假设我想编写一段代码,在运行时可以接收共享相同接口(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 {
}
}
错误:
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_ptr
和std::dynamic_pointer_cast<SpecificDecoder1>
等等。这就是我想做的。
最佳答案
可以从Arc<Mutex<dyn Trait>>
向下转型进入Arc<Mutex<Type>>
虽然你会在下面看到它需要一些 unsafe
这不是一个很好的工作流程。我鼓励在此之前探索其他途径(并且一般免责声明,向下转型通常是一种代码味道,表明抽象定义不明确)。
无论如何,一个关键部分是从 Arc<A>
进行强制转换至Arc<B>
可能如果 A
和B
兼容 - 意味着它们具有相同的大小、对齐方式并符合其他标准,就像执行 std::mem::transmute
一样(同样的 Drop
行为是理想的,尽管技术上不需要)。如果满足这些标准,则通过 Arc::into_raw
完成此转换。和 Arc::from_raw
中间有一个指针。请参阅有关后者的文档以了解更多信息。
我们还知道从 Mutex<dyn Trait>
转变而来至Mutex<Type>
是安全(如果 dyn Trait
是 Type
),因为 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/