dyn
型をアップキャストするとエラーになる。
以下では、dyn
型から dyn
型へのキャストでエラーになっている。
fn main() {
let some = &0 as &dyn SomeTrait;
let _base = some as &dyn BaseTrait;
}
// -- BaseTrait -- //
trait BaseTrait {}
impl<T> BaseTrait for T {}
// -- SomeTrait -- //
trait SomeTrait: BaseTrait {}
impl<T: BaseTrait> SomeTrait for T {}
error[E0658]: cannot cast `dyn SomeTrait` to `dyn BaseTrait`, trait upcasting coercion is experimental --> src\main.rs:3:16 | 3 | let _base = some as &dyn BaseTrait; | ^^^^ | = note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information = note: required when coercing `&dyn SomeTrait` into `&dyn BaseTrait`
幾つか方法がある。
※ 方法 3 が最も汎用的なので迷ったらそれを選ぶとよい。
アップキャスト用のメソッドを派生側のトレイトに作る。
つまり、キャストの位置をトレイトの実装内へと移す。これにより、キャスト対象の静的型が分かっている箇所でキャストできるため、静的型から動的型へのキャストとなり、エラーにはならなくなる。
以下では、SomeTrait
にアップキャスト用のメソッド as_base
を定義している。
fn main() {
let some = &0 as &dyn SomeTrait;
let _base = some.as_base();
}
// -- BaseTrait -- //
trait BaseTrait {}
impl<T> BaseTrait for T {}
// -- SomeTrait -- //
trait SomeTrait: BaseTrait {
fn as_base(&self) -> &dyn BaseTrait;
}
impl<T: BaseTrait> SomeTrait for T {
fn as_base(&self) -> &dyn BaseTrait {
self
}
}
アップキャスト用メソッドを派生側ではなく基底側に作っておく。
この方法は、複数の派生トレイトに楽に対応できる。
ただし、基底側が定義済だと使えない。
以下では、BaseTrait
にアップキャスト用のメソッド as_base
を定義している。
fn main() {
let some = &0 as &dyn SomeTrait;
let _base = some.as_base();
}
// -- BaseTrait -- //
trait BaseTrait {
fn as_base(&self) -> &dyn BaseTrait;
}
impl<T> BaseTrait for T {
fn as_base(&self) -> &dyn BaseTrait {
self
}
}
// -- SomeTrait -- //
trait SomeTrait: BaseTrait {}
impl<T: BaseTrait> SomeTrait for T {}
予めアップキャスト用のトレイトを作っておく。
この方法は、複数の派生トレイトに楽に対応できる。
また、基底側が定義済でも使える。
以下では、定義済の Any
へのアップキャスト用に AsAny
を用意している。
use std::any::Any;
fn main() {
let some = &0 as &dyn SomeTrait;
let _any = some.as_any();
}
// -- AsAny -- //
trait AsAny: Any {
fn as_any(&self) -> &dyn Any;
}
impl<T: Any> AsAny for T {
fn as_any(&self) -> &dyn Any {
self
}
}
// -- SomeTrait -- //
trait SomeTrait: Any + AsAny {
fn test(&self) {
let _any = self.as_any();
}
}
impl<T: Any> SomeTrait for T {}
上記では、不変参照を通したアップキャストについて説明したが、他にも可変参照や Box
を通した関数があると便利 (Box
以外のスマートポインタ、例えば Rc
などは、Box
から From
トレイトで変換できるため、事前に関数を用意しておく必要性は低い)。
以下では、前述の『方法 3』のサンプルの AsAny
を強化している。
trait AsAny: Any {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn as_any_box(self: Box<Self>) -> Box<dyn Any>;
}
impl<T: Any> AsAny for T {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn as_any_box(self: Box<Self>) -> Box<dyn Any> {
self
}
}