Rust - downcast dyn trait [duplicate] - rust

How do I get Box<B> or &B or &Box<B> from the a variable in this code:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
This code returns an error:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait

There are two ways to do downcasting in Rust. The first is to use Any. Note that this only allows you to downcast to the exact, original concrete type. Like so:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
The other way is to implement a method for each "target" on the base trait (in this case, A), and implement the casts for each desired target type.
Wait, why do we need as_any?
Even if you add Any as a requirement for A, it's still not going to work correctly. The first problem is that the A in Box<dyn A> will also implement Any... meaning that when you call downcast_ref, you'll actually be calling it on the object type A. Any can only downcast to the type it was invoked on, which in this case is A, so you'll only be able to cast back down to &dyn A which you already had.
But there's an implementation of Any for the underlying type in there somewhere, right? Well, yes, but you can't get at it. Rust doesn't allow you to "cross cast" from &dyn A to &dyn Any.
That is what as_any is for; because it's something only implemented on our "concrete" types, the compiler doesn't get confused as to which one it's supposed to invoke. Calling it on an &dyn A causes it to dynamically dispatch to the concrete implementation (again, in this case, B::as_any), which returns an &dyn Any using the implementation of Any for B, which is what we want.
Note that you can side-step this whole problem by just not using A at all. Specifically, the following will also work:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
However, this precludes you from having any other methods; all you can do here is downcast to a concrete type.
As a final note of potential interest, the mopa crate allows you to combine the functionality of Any with a trait of your own.

It should be clear that the cast can fail if there is another type C implementing A and you try to cast Box<C> into a Box<B>. I don't know your situation, but to me it looks a lot like you are bringing techniques from other languages, like Java, into Rust. I've never encountered this kind of Problem in Rust -- maybe your code design could be improved to avoid this kind of cast.
If you want, you can "cast" pretty much anything with mem::transmute. Sadly, we will have a problem if we just want to cast Box<A> to Box<B> or &A to &B because a pointer to a trait is a fat-pointer that actually consists of two pointers: One to the actual object, one to the vptr. If we're casting it to a struct type, we can just ignore the vptr. Please remember that this solution is highly unsafe and pretty hacky -- I wouldn't use it in "real" code.
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
EDIT: Screw that, it's even more unsafe than I thought. If you want to do it correctly this way you'd have to use std::raw::TraitObject. This is still unstable though. I don't think that this is of any use to OP; don't use it!
There are better alternatives in this very similar question: How to match trait implementors

Related

Conflicting trait implementations for types that clearly already implement the trait

I'm working with webassembly so I need to fetch the pointer to a buffer. In the case where T is just AsRef<[f32]> (that is, it can be converted to a slice if I'm understanding correctly), I have solved it like this:
#[derive(TS, Serialize)]
pub struct PtrBufF32(usize);
impl<T> From<T> for PtrBufF32
where T: AsRef<[f32]>
{
fn from(f32arr: T) -> Self {
let slc: &[f32] = f32arr.as_ref();
let ptr: *const f32 = slc.as_ptr();
Self(ptr as usize)
}
}
I had help from another stack overflow user to understand what's going on -- as far as I'm understanding, this means "for any T that can be converted to a refence of &[f32] (that is, a slice), then we can implement this trait. The result is simply the pointer to the start of the slice of course.
But then in addition to implementing for anything that can be represented as &[f32], we need to implement for any collection of things that can be represented by &[f32]. Like, if my type T implements Into<&[f32]>, then I can implement the type for any AsRef<[T]>, right? And so on. Any collection of those also implements it. So I thought:
impl<T> From<T> for PtrBufF32
where T: AsRef<[dyn Into<PtrBufF32>]>
{
fn from(f32arr: T) -> Self {
todo!()
}
}
But no… apparently those are "conflicting implementations" somehow?
error[E0119]: conflicting implementations of trait `std::convert::From<memory::ptrbuf::PtrBufF32>` for type `memory::ptrbuf::PtrBufF32`
Yet, if I try to PtrBufF32::from(vec![T]) and T implements Into<AsRef<[f32]>>, it doesn't let me. So clearly it's not conflicting, is it?
Thanks
As long as you implemented conversion for undefined list of types you should think a little bit more widely.
Imagine some type ForeignType, that implements both Into<PtrBufF32> and AsRef<[f32]>. Then if you call PtrBufF32::from(my_foreign_type), compiler cannot decide which of two implementation he must use.

Incorrect type inference for Rust vector of trait object

I am having trouble understanding the following error:
// test.rs
struct A {
a: u32
}
trait B { }
impl B for A { }
struct C {
c: Vec<Box<dyn B>>,
}
fn test() {
let a = A {a: 1};
let a_vec = vec![Box::new(a)];
let c = C {
c: a_vec,
// c: vec![Box::new(a)],
};
}
Compile error:
mismatched types
expected trait object `dyn test::B`, found struct `test::A`
The error occurs on the line where I try to create C. What's interesting is that if I create C in the following way then it compiles alright.
let c = C {
c: vec![Box::new(a)],
};
Another way that would work is
let a_vec: Vec<Box<dyn B>> = vec![Box::new(a)];
let c = C {
c: a_vec,
};
One other experiment I did is to change the type of C.c to just Box instead of Vec<Box>, and then it compiles no matter how I initiate it.
Seems to me this might be some missing feature/bug of Rust's type inference regarding vector of trait objects? I am still very new to Rust so any idea on this is really appreciated!
This is all expected behaviour. A Box<A> can be coerced into a Box<dyn B> – this is called an unsized coercion, and it happens implicitly whenever the compiler knows that the target type is Box<dyn B>. However, when just writing
let a_vec = vec![Box::new(a)];
the compiler doesn't know the item type of the vector, so it infers it from the expression on the right-hand side, meaning that a_vec ends up with the type Vec<Box<A>>.
There is no unsized coercion from Vec<Box<A>> to Vec<Box<dyn B>>. Converting Box<A> to Box<dyn B> simply means converting a pointer to a fat pointer on the stack, which is a cheap operation. Converting vectors of these elements is very different – it would require reallocating the whole vector and unsizing each element, which is a rather expensive operation, so it should never happen implicitly.
In all the versions that actually compile, the vector is created as a Vec<Box<dyn B>> right from the get-go, since you tell the compiler that's the type you want.

What is a canonical approach for having lite concrete-specific behaviour in Rust? [duplicate]

How do I get Box<B> or &B or &Box<B> from the a variable in this code:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
This code returns an error:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
There are two ways to do downcasting in Rust. The first is to use Any. Note that this only allows you to downcast to the exact, original concrete type. Like so:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
The other way is to implement a method for each "target" on the base trait (in this case, A), and implement the casts for each desired target type.
Wait, why do we need as_any?
Even if you add Any as a requirement for A, it's still not going to work correctly. The first problem is that the A in Box<dyn A> will also implement Any... meaning that when you call downcast_ref, you'll actually be calling it on the object type A. Any can only downcast to the type it was invoked on, which in this case is A, so you'll only be able to cast back down to &dyn A which you already had.
But there's an implementation of Any for the underlying type in there somewhere, right? Well, yes, but you can't get at it. Rust doesn't allow you to "cross cast" from &dyn A to &dyn Any.
That is what as_any is for; because it's something only implemented on our "concrete" types, the compiler doesn't get confused as to which one it's supposed to invoke. Calling it on an &dyn A causes it to dynamically dispatch to the concrete implementation (again, in this case, B::as_any), which returns an &dyn Any using the implementation of Any for B, which is what we want.
Note that you can side-step this whole problem by just not using A at all. Specifically, the following will also work:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
However, this precludes you from having any other methods; all you can do here is downcast to a concrete type.
As a final note of potential interest, the mopa crate allows you to combine the functionality of Any with a trait of your own.
It should be clear that the cast can fail if there is another type C implementing A and you try to cast Box<C> into a Box<B>. I don't know your situation, but to me it looks a lot like you are bringing techniques from other languages, like Java, into Rust. I've never encountered this kind of Problem in Rust -- maybe your code design could be improved to avoid this kind of cast.
If you want, you can "cast" pretty much anything with mem::transmute. Sadly, we will have a problem if we just want to cast Box<A> to Box<B> or &A to &B because a pointer to a trait is a fat-pointer that actually consists of two pointers: One to the actual object, one to the vptr. If we're casting it to a struct type, we can just ignore the vptr. Please remember that this solution is highly unsafe and pretty hacky -- I wouldn't use it in "real" code.
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
EDIT: Screw that, it's even more unsafe than I thought. If you want to do it correctly this way you'd have to use std::raw::TraitObject. This is still unstable though. I don't think that this is of any use to OP; don't use it!
There are better alternatives in this very similar question: How to match trait implementors

Retrieving generic struct from trait object [duplicate]

How do I get Box<B> or &B or &Box<B> from the a variable in this code:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
This code returns an error:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
There are two ways to do downcasting in Rust. The first is to use Any. Note that this only allows you to downcast to the exact, original concrete type. Like so:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
The other way is to implement a method for each "target" on the base trait (in this case, A), and implement the casts for each desired target type.
Wait, why do we need as_any?
Even if you add Any as a requirement for A, it's still not going to work correctly. The first problem is that the A in Box<dyn A> will also implement Any... meaning that when you call downcast_ref, you'll actually be calling it on the object type A. Any can only downcast to the type it was invoked on, which in this case is A, so you'll only be able to cast back down to &dyn A which you already had.
But there's an implementation of Any for the underlying type in there somewhere, right? Well, yes, but you can't get at it. Rust doesn't allow you to "cross cast" from &dyn A to &dyn Any.
That is what as_any is for; because it's something only implemented on our "concrete" types, the compiler doesn't get confused as to which one it's supposed to invoke. Calling it on an &dyn A causes it to dynamically dispatch to the concrete implementation (again, in this case, B::as_any), which returns an &dyn Any using the implementation of Any for B, which is what we want.
Note that you can side-step this whole problem by just not using A at all. Specifically, the following will also work:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
However, this precludes you from having any other methods; all you can do here is downcast to a concrete type.
As a final note of potential interest, the mopa crate allows you to combine the functionality of Any with a trait of your own.
It should be clear that the cast can fail if there is another type C implementing A and you try to cast Box<C> into a Box<B>. I don't know your situation, but to me it looks a lot like you are bringing techniques from other languages, like Java, into Rust. I've never encountered this kind of Problem in Rust -- maybe your code design could be improved to avoid this kind of cast.
If you want, you can "cast" pretty much anything with mem::transmute. Sadly, we will have a problem if we just want to cast Box<A> to Box<B> or &A to &B because a pointer to a trait is a fat-pointer that actually consists of two pointers: One to the actual object, one to the vptr. If we're casting it to a struct type, we can just ignore the vptr. Please remember that this solution is highly unsafe and pretty hacky -- I wouldn't use it in "real" code.
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
EDIT: Screw that, it's even more unsafe than I thought. If you want to do it correctly this way you'd have to use std::raw::TraitObject. This is still unstable though. I don't think that this is of any use to OP; don't use it!
There are better alternatives in this very similar question: How to match trait implementors

"Expected associated type, found `u32`" when using the lifetime of a parameter as trait parameter in where bound

I tried to compile this code (Playground):
trait Family<'a> {
type Out;
}
struct U32Family;
impl<'a> Family<'a> for U32Family {
type Out = u32;
}
trait Iterator {
type Item;
fn next<'s>(&'s mut self) -> <Self::Item as Family<'s>>::Out
where
Self::Item: Family<'s>;
}
struct Foo;
impl Iterator for Foo {
type Item = U32Family;
fn next<'s>(&'s mut self) -> <Self::Item as Family<'s>>::Out
where
Self::Item: Family<'s>,
{
0u32 // <-- in real code, this is somehow calculated
}
}
But sadly, it results in this error:
error[E0308]: mismatched types
--> src/main.rs:28:9
|
24 | fn next<'s>(&'s mut self) -> <Self::Item as Family<'s>>::Out
| ------------------------------- expected `<U32Family as Family<'s>>::Out` because of return type
...
28 | 0u32
| ^^^^ expected associated type, found u32
|
= note: expected type `<U32Family as Family<'s>>::Out`
found type `u32`
I really don't understand why. Obviously, in this code snippet, <U32Family as Family<'s>>::Out is exactly u32. But Rust seems to think that it's not always the same. Why? And how can I make it compile?
Some notes:
There are a bunch of similar situations where a similar error occurs, but I think this is different from everything I've seen so far.
I cannot use type Out: for<'a> Family<'a>;. So that's not a workaround that works for me.
If I remove the lifetime parameter of Family, everything works.
If I replace Family<'s> with Family<'static> in the function signature, everything works.
EDIT: I can work around this problem by adding:
impl U32Family {
fn from<'a>(v: u32) -> <Self as Family<'a>>::Out {
v
}
}
Then I can just say Self::Item::from(0u32) in the body of next(). (Playground)
I think it's clear why the error in next() is gone: U32Family::from always takes u32 as argument. Hardcoded. Never changing. The bigger question about this workaround is: why does the from() method compile fine? So in from() the compiler somehow knows that <Self as Family<'a>>::Out is always u32, but if I try the same in next(), somehow the compiler doesn't understand that <Self::Item as Family<'s>>::Out is u32. Now I'm even more confused.
EDIT2: first, I suspected that specialization is the problem. For example, you might write:
impl Family<'static> for U32Family {
type Out = char;
}
Then of course, the compiler would be right in assuming that u32 is not always the same as <Self::Item as Family<'s>>::Out for any 's. However, I think this is not the problem.
First of all, impls that can be specialized need to be marked with the default keyword. I did not do that, so I should be able to assume the associated type is in fact u32 (the RFC talks about something very similar). But additionally, specialization based on lifetimes is not allowed.
So by now I tend to think this is a compiler error. But I'd love to get another answer!
I think the problem is that it is a "coincidence" that <Self::Item as Family<'s>>::Out is u32 for all 's. The compiler can prove it for any 's you want, but it can't even express the concept that it is true for all 's.
The work-around you have found is the right approach: add a method to U32Family which converts a u32 into a <Self as Family<'a>>::Out. The body of the method is entirely inside the scope of 'a, so the compiler can prove that the conversion is type-correct for that 'a, and therefore that the method is type-correct. Then, at the call-site, you're telling the compiler to use its knowledge about the method.
struct U32Family;
...
impl Iterator for Foo {
type Item = U32Family;
So next() must return Option<U32Family>, whose only possible values are None and Some(U32Family{})
You probably want Item = <U32Family as Family<'static>::Out which fixes this issue but creates some lifetime issues. (The Item needs a lifetime because Family has one, but you only accept a lifetime on next())

Resources