1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rustで多態性を表現する(静的ディスパッチ)

Posted at

Rustでプログラミングをしているとオブジェクト指向のように多態性を表現したくなります
よくある動物が鳴くやつをtraitで実現してみます

/// 鳴くトレイト
trait Bark {
    fn bark(&self) -> ();
}

/// 犬
struct Dog;
impl Bark for Dog {
    fn bark(&self) -> () {
        println!("わんわん!");
    }
}

/// 猫
struct Cat;
impl Bark for Cat {
    fn bark(&self) -> () {
        println!("にゃーにゃー!");
    }
}

これをオブジェクト指向よろしく扱ってみます

fn main() {
    let mut animal: Box<dyn Bark> = Box::new(Dog);

    animal.bark(); // "わんわん!"

    animal = Box::new(Cat);

    animal.bark(); // "にゃーにゃー!"
}

ここで気になるのが動的ディスパッチになっていることです
Box<dyn Bark>のところですね
トレイトオブジェクトを呼び出す際にメソッドを解決するのに実行コストがかかるというのが定説です(静的ディスパッチ比)

そういわれるとなんとなく避けたいなぁとなってしまうのが人情でしょう(?)
動的ディスパッチを避けるためにはdyn Barkを使用しないようにしないといけません

そこで最近覚えたのがenumによるラップです
以下のようにenumを宣言します

enum Animal {
    Dog(Dog),
    Cat(Cat)
}
impl Bark for Animal {
    fn bark(&self) -> () {
        match self {
            Animal::Dog(inner) => inner.bark(),
            Animal::Cat(inner) => inner.bark(),
        }
    }
}

enum Animalを定義してDogCatと同様にBarkトレイトを実装し、
それぞれのパターンごとにbark()を実行するわけです
これにより動的解決が回避され、静的ディスパッチになります

あとは以下のように呼び出せばよいですね

    let mut animal: Animal = Animal::Dog(Dog);

    animal.bark(); // "わんわん!"

    animal = Animal::Cat(Cat);

    animal.bark(); // "にゃーにゃー!"

これで静的ディスパッチが行えるようになりました
やはり動的ディスパッチと比べると一手間かかってしまうのが難点ですが、
パフォーマンスが大事なシステムなどでやっておくに越したことはないでしょう(?)

上記のやり方であればオブジェクト指向らしくデータと処理をまとめておけるので、
処理の見通しもよく、メンテもしやすいんじゃないでしょうか
ほかのやり方もあるかもしれないのでもっと探究していきたいですね🚀

今日のplayground

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?