dyn
型 (トレイトオブジェクト) はそれ自体に実装を付加できる。
代表例は、impl dyn MyTrait { ... }
のような固有機能の実装パターン。
または、impl SomeTrait for dyn MyTrait { ... }
のようなトレイトの実装パターン。
ダウンキャスト系のメソッドを実装する場合が多い。
以下では、dyn Animal
を Dog
にダウンキャストしている。
use std::any::Any;
fn main() {
let animal = &(Dog {}) as &dyn Animal;
let dog = animal.downcast::<Dog>();
dog.trick();
}
// -- Animal -- //
trait Animal: Any {}
impl dyn Animal {
fn downcast<T: Animal>(&self) -> &T {
(self as &dyn Any).downcast_ref().unwrap()
}
}
// -- Dog -- //
struct Dog {}
impl Animal for Dog {}
impl Dog {
fn trick(&self) {
println!("Bow!");
}
}
オブジェクトセーフでないトレイトを動的に活用できるようにする。
以下では、PartialEq
トレイトの eq
メソッドにより、数値型または文字列型をとる dyn Id
どうしを比較している。なお、eq
は比較先に Self
を用いる。そのため、PartialEq
はこの手法なしでは、異なる型どうしの比較も、動的型の処理もできない (オブジェクトセーフの条件を満たせないため)。
fn main() {
let x = &1 as &dyn Id;
let y = &"1".to_string() as &dyn Id;
assert!(!x.eq(y));
}
trait Id {
fn key(&self) -> String;
fn is(&self, other: &dyn Id) -> bool {
self.key() == other.key()
}
}
impl PartialEq for dyn Id {
fn eq(&self, other: &Self) -> bool {
self.is(other)
}
}
impl Id for i32 {
fn key(&self) -> String { format!("i32: {self}") }
}
impl Id for String {
fn key(&self) -> String { format!("str: {self}") }
}
上記を応用する上で、つい忘れがちな点について。
dyn TraitName
は糖衣構文であり、dyn TraitName + 'static
と等価である。
dyn TraitName + '_
のように匿名ライフタイムを使えば、'static
境界が解除される。