Box<B>
または&B
または&Box<B>
このコードのa
変数から:
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>;
}
このコードはエラーを返します:
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
Rustでダウンキャストを行うには2つの方法があります。最初は Any
を使用することです。このonlyを使用すると、元の正確なコンクリートタイプにダウンキャストできます。そのようです:
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!"),
};
}
もう1つの方法は、基本特性(この場合はA
)の各「ターゲット」にメソッドを実装し、目的の各ターゲットタイプにキャストを実装することです。
待って、なぜas_any
が必要なのですか?
Any
の要件としてA
を追加しても、まだ正しく機能しません。最初の問題は、Box<dyn A>
のA
がalsoAny
...を実装するということです。つまり、downcast_ref
を呼び出すと、実際にオブジェクト型A
で呼び出しています。 Any
canonly呼び出された型にダウンキャストします。この場合はA
なので、できるのは既に持っていた&dyn A
に戻すには。
しかし、somewhereに、基礎となる型のAny
の実装がありますか?まあ、はい、しかしあなたはそれを得ることができません。 Rustは、&dyn A
から&dyn Any
への「クロスキャスト」を許可しません。
Thatはas_any
の目的です。これは「具象」型にのみ実装されているものであるため、コンパイラはどの型を呼び出すべきかについて混乱することはありません。 &dyn A
で呼び出すと、具体的な実装(この場合もB::as_any
)に動的にディスパッチされ、Any
for B
の実装を使用して&dyn Any
が返されます。欲しいです。
canA
at allを使用しないことで、この問題全体を回避できます。 。具体的には、alsoが機能します。
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!")
};
}
ただし、これにより、otherメソッドを使用できなくなります。allここでできることは、具体的なタイプにダウンキャストされます。
潜在的な関心の最後のメモとして、 mopa crateを使用すると、Any
の機能と独自の特性を組み合わせることができます。
C
を実装する別のタイプA
があり、Box<C>
をBox<B>
にキャストしようとすると、キャストが失敗する可能性があることは明らかです。あなたの状況はわかりませんが、Javaのような他の言語の技術をRustに持ち込んでいるように見えます。 Rust-この種の問題を回避するためにコード設計を改善できるかもしれません。
必要に応じて、 mem::transmute
でほとんど何でも「キャスト」できます。悲しいことに、Box<A>
をBox<B>
にキャストしたい場合、または&A
を&B
にキャストしたい場合、trait
へのポインターが太いため、問題が発生します。 -実際には2つのポインターで構成されるポインター:1つは実際のオブジェクトへ、もう1つはvptrへ。 struct
型にキャストする場合、vptrを無視できます。このソリューションは非常に安全ではなく、かなりハッキングされていることを覚えておいてください。「実際の」コードでは使用しません。
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
編集:それを台無しに、それは私が思ったよりもさらに安全ではありません。このように正しく行うには、 std::raw::TraitObject
を使用する必要があります。しかし、これはまだ不安定です。これがOPに役立つとは思いません。使用しないでください!
この非常によく似た質問には、より良い代替案があります。 特性実装者との一致方法