There are definitively other ways to solve this problem but I was just curious whether there was any way to make the following code work in Rust in some way I do not know yet.
The following code sample ( playground )
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);
}
will fail to compile with the following error:
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
There is definitely a way to "cast" a boxed trait object back to its concrete type like shown here , but this is basically the other way around. I come from a C++ background, so I am familiar with this type of API where a derived object can be passed as a base class.
The other possible solution I mentioned and used was to have a wrapper struct which has the SharedFooTraitObject
as a field and implements the magic_and_then_some()
operation on top. One can then pass that wrapper struct around and clone the Arc<Mutex>
ed field for library code which expects only the trait object.
I was just curious whether this type of type coercion / casting is possible in Rust.
Kind Regards, RM
I'm not certain I understand correctly what you want; sorry if I'm wrong.
I removed all the wrapper types in order to focus on reference to concrete type vs reference to dyn trait.
Providing a reference to a concrete type where a reference to a dyn trait is expected is implicitly handled by the language because all the information is provided at compile time. A fat pointer pointing to the appropriate virtual table can be provided since the concrete type is well known.
On the other hand, when using a dyn trait, the compiler does not know anymore the original type, then providing a reference to a dyn trait where a reference to a concrete type is expected is impossible.
If we really want to achieve this, we need some runtime check. The Any
trait can help for this purpose. When we downcast , we are certain of the concrete type, then we can obtain a reference to this concrete type.
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 • •
*/
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.