如何将具体的盒装对象转换为盒装特征对象



肯定还有其他方法可以解决这个问题,但我只是好奇是否有任何方法可以让以下代码以某种方式在 Rust 中工作我还不知道。

以下代码示例(游乐场)

use std::sync::{Arc, Mutex};
trait ProvidesFoo {
fn magic(&mut self);
}
struct Foo {
magic_value: u32
}
impl Default for Foo {
fn default() -> Self {
Self {magic_value: 42}
}
}
impl ProvidesFoo for Foo {
fn magic(&mut self) {
println!("ProvidesFoo magic {}", self.magic_value);
}
}
pub type SharedFooConcrete = Arc<Mutex<Box<Foo>>>;
pub type SharedFooTraitObj = Arc<Mutex<Box<dyn ProvidesFoo + Send + Sync>>>;
struct FooProvider {
foo_as_trait_obj: SharedFooTraitObj
}
impl FooProvider {
fn magic_and_then_some(&mut self) {
let mut fooguard = self.foo_as_trait_obj.lock().unwrap();
fooguard.magic();
println!("Additional magic");
}
}
fn uses_shared_foo_boxed_trait_obj(foo: SharedFooTraitObj) {
let mut foo_provider = FooProvider {
foo_as_trait_obj: foo
};
foo_provider.magic_and_then_some();
}
fn uses_shared_foo_concrete(foo: SharedFooConcrete) {
let mut fooguard = foo.lock().unwrap();
fooguard.magic();
}
fn main() {
let shared_foo = Arc::new(Mutex::new(Box::new(Foo::default())));
uses_shared_foo_concrete(shared_foo.clone());
uses_shared_foo_boxed_trait_obj(shared_foo);
}

将无法编译并显示以下错误:

error[E0308]: mismatched types
--> fsrc-example/src/bin/test2.rs:52:37
|
52 |     uses_shared_foo_boxed_trait_obj(shared_foo);
|     ------------------------------- ^^^^^^^^^^ expected trait object `dyn ProvidesFoo`, found struct `Foo`
|     |
|     arguments to this function are incorrect
|
= note: expected struct `Arc<Mutex<Box<(dyn ProvidesFoo + Send + Sync + 'static)>>>`
found struct `Arc<Mutex<Box<Foo>>>`
note: function defined here

肯定有一种方法可以将盒装特征对象"投射"回其具体类型,如下所示,但这基本上是相反的方法。我来自C++背景,所以我熟悉这种类型的 API,其中派生对象可以作为基类传递。

我提到并使用的另一个可能的解决方案是拥有一个包装结构,该结构将SharedFooTraitObject作为字段并在顶部实现magic_and_then_some()操作。然后,可以传递该包装器结构,并为仅需要 trait 对象的库代码克隆Arc<Mutex>ed 字段。

我只是好奇这种类型的强制/铸造在 Rust 中是否可行。

亲切问候 .RM

我不确定我是否正确理解你想要什么;对不起,如果我错了。

我删除了所有包装器类型,以便专注于对具体类型的引用与对 dyn 特性的引用。

提供对具体类型的引用,其中预期对 dyn 特征的引用由语言隐式处理,因为所有信息都是在编译时提供的。 可以提供指向相应虚拟表的胖指针,因为具体类型是众所周知的。

另一方面,当使用dyn trait时,编译器不再知道原始类型,因此不可能提供对dyn trait的引用,其中需要对具体类型的引用。

如果我们真的想实现这一点,我们需要一些运行时检查。Any性状可以为此目的提供帮助。 当我们向下投射时,我们确定具体类型,然后我们可以获得对该混凝土类型的引用。

现在,当涉及到Arc<Mutex<Box<Foo>>>vsArc<Mutex<Box<dyn ProvidesFoo + Send + Sync>>>时,不会发生隐式或显式转换,因为这不仅仅是胖指针和普通指针之间的转换问题:所有权和同步与MutexArc一起发挥作用。 我们不能克隆Arc并期望新的包含一种与原始指针不同的指针;如果我们可以在内部构建另一个带有预期指针类型的Arc,那么第二个Arc将完全独立于原始,因此这将是关于所有权的完全不同的语义。

我只是建议你在 API 需要时到处保留Arc<Mutex<Box<dyn ProvidesFoo + Send + Sync>>>,并且一旦你需要返回到具体类型,你就会获取它包含的动态引用,并使用本示例中所示Any的解决方案。

use std::any::Any;
trait MyTrait: Any {
fn show(&self);
fn action(&mut self);
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
struct MyStructA {
value: i32,
}
impl MyStructA {
fn new(value: i32) -> Self {
Self { value }
}
}
impl MyTrait for MyStructA {
fn show(&self) {
println!("value is {}", self.value);
}
fn action(&mut self) {
self.value += 1;
self.show();
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
struct MyStructB {
txt: String,
}
impl MyStructB {
fn new(txt: String) -> Self {
Self { txt }
}
}
impl MyTrait for MyStructB {
fn show(&self) {
println!("txt is {}", self.txt);
}
fn action(&mut self) {
self.txt.push_str(" •");
self.show();
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
fn use_impl_trait(t: &mut impl MyTrait) {
print!("impl: ");
t.action();
}
fn use_dyn_trait(t: &mut dyn MyTrait) {
print!("dyn: ");
t.action();
}
fn main() {
{
println!("~~~~ using concrete types ~~~~");
let mut msa = MyStructA::new(100);
let msa_ref = &mut msa;
use_dyn_trait(msa_ref);
use_impl_trait(msa_ref);
let mut msb = MyStructB::new("xxx".to_owned());
let msb_ref = &mut msb;
use_dyn_trait(msb_ref);
use_impl_trait(msb_ref);
}
{
println!("~~~~ using dynamic types ~~~~");
let mut msa = MyStructA::new(200);
let msa_dyn_ref: &mut dyn MyTrait = &mut msa;
use_dyn_trait(msa_dyn_ref);
if let Some(msa_ref) =
msa_dyn_ref.as_any_mut().downcast_mut::<MyStructA>()
{
use_impl_trait(msa_ref);
}
let mut msb = MyStructB::new("yyy".to_owned());
let msb_dyn_ref: &mut dyn MyTrait = &mut msb;
use_dyn_trait(msb_dyn_ref);
if let Some(msb_ref) =
msb_dyn_ref.as_any_mut().downcast_mut::<MyStructB>()
{
use_impl_trait(msb_ref);
}
}
}
/*
~~~~ using concrete types ~~~~
dyn: value is 101
impl: value is 102
dyn: txt is xxx •
impl: txt is xxx • •
~~~~ using dynamic types ~~~~
dyn: value is 201
impl: value is 202
dyn: txt is yyy •
impl: txt is yyy • •
*/

最新更新