dyn 型 (トレイトオブジェクト) はそれ自体に実装を付加できる。

代表例は、impl dyn MyTrait { ... } のような固有機能の実装パターン。

または、impl SomeTrait for dyn MyTrait { ... } のようなトレイトの実装パターン。

固有機能の実装

主な用途

ダウンキャスト系のメソッドを実装する場合が多い。

サンプル

以下では、dyn AnimalDog にダウンキャストしている。


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 境界が解除される。