Reputation: 15809
In Rust, I'm able to downcast a trait object to a concrete type using .as_any().downcast_ref(). That works ok, but it returns a &ref, not the owned object itself.
How can I get ownership of a downcast object?
Update: In response to the comments, here is what I was trying to do:
trait Foo {
fn hello() -> String;
fn as_any(&self) -> &dyn Any;
}
struct Bar {}
impl Foo for Bar {
fn hello() -> String {
"hello".to_owned()
}
fn as_any(&self) -> &dyn Any {
self
}
}
fn some_func(foo: impl Foo) {
// I want a Bar here, not a &Bar
let bar_opt = foo.as_any().downcast_ref::<Bar>();
}
I can see why .as_any() would not work, because it returns a reference.
Upvotes: 0
Views: 610
Reputation: 60493
Normally when talking about downcasting to a owned type you'd have an owned trait object like Box<dyn Trait>
. Your original code allows you to get a reference to dyn Any
, but to get a Box<dyn Any>
from your trait object you can take Box<Self>
as the receiver. Like this:
trait Foo {
fn hello() -> String;
fn into_any(self: Box<Self>) -> Box<dyn Any> where Self: Sized + 'static {
self
}
}
Then you can call .into_any()
on a Box<dyn Trait>
. And then J. Kadditz's answer is right that you can just .downcast()
it to your desired type.
However in your some_func
sample, you don't actually have a trait object; you have a generic type that implements the trait. You could use the method above to shove it into a Box
use dynamic dispatch to get it as Box<dyn Any>
and then downcast and dereference it back to the desired type, but that in principle should be unnecessary.
You can't go straight from owned value to owned value through Any
since it only really provides for dynamicism but here's an unsafe
way using TypeId
(which is how Any
is safe) that I think should work:
use std::any::TypeId;
trait Foo {
fn hello() -> String;
fn downcast<T: 'static>(self) -> Result<T, Self> where Self: Sized + 'static {
if TypeId::of::<T>() == TypeId::of::<Self>() {
let t = unsafe { std::ptr::read(&self as *const _ as *const T) };
std::mem::forget(self);
Ok(t)
} else {
Err(self)
}
}
}
Then using it in your function would look like this:
fn some_func(foo: impl Foo + 'static) {
if let Ok(bar) = foo.downcast::<Bar>() {
// do stuff with bar
}
}
Upvotes: 1
Reputation: 433
This is achieved using Box and the downcast() method, which consumes the boxed trait object and returns a 'Result' containing either the downcasted object or an error if the types don't match. I would need your specific code to provide an implementation for your situation, but here's an example of its use:
use std::any::Any;
let animal: Box<dyn Any> = Box::new("Dog");
if let Ok(dog) = animal.downcast::<String>() {
println!("It's a dog named {}", dog);
} else {
println!("Not a dog");
}
The code tries to downcast a trait object 'animal' to a String, allowing us to use String methods if successful
Upvotes: 2