LoginSignup
2
0

More than 3 years have passed since last update.

複数のトレイト境界を持つトレイトオブジェクトの取り扱い

Posted at

はじめに

複数のトレイト境界を持つトレイトオブジェクトを取り扱ったので忘備録として投稿致します。
ソースコードはgistにあります。

やりたいこと

複数のトレイトを実装した型を共通したインターフェイスで扱いたい

問題

例えば以下のようなトレイトA,Bがある場合、

trait A {
    fn fn_a(&self) {
        println!("fna");
    }
}

trait B {
    fn fn_b(&self) {
        println!("fnb");
    }
}

以下のように、合成したトレイトオブジェクトは使用できません。

fn main() {
    let mut st_vec: Vec<Box<dyn A + B>> = Vec::new();
    st_vec2.push(Box::new(St1{}));
    st_vec2.push(Box::new(St2{}));

    for s in st_vec2.iter() {
        s.fn_a();
        s.fn_b();
    }
}

解決

そこで、トレイトABを合成したトレイトCを導入します。

trait C: A + B {}

合成したトレイトと使用した例は以下のようになります。

struct St1{}
impl A for St1{}
impl B for St1{}
impl C for St1{}

struct St2{}
impl A for St2{}
impl B for St2{}
impl C for St2{}

fn main() {
    //let mut st_vec: Vec<Box<dyn A + B>> = Vec::new(); 使用できない
    let mut st_vec2: Vec<Box<dyn C>> = Vec::new();
    st_vec2.push(Box::new(St1{}));
    st_vec2.push(Box::new(St2{}));

    for s in st_vec2.iter() {
        s.fn_a();
        s.fn_b();
    }
}

結果

既存のトレイトABを合成したトレイトCを導入することにより合成したトレイトオブジェクトが作成できるため、インターフェイスを共通化することができました

おまけ:発展した実装例

犬猫の生き死にはよく、オブジェクト指向を説明する際に使用されます。
今回はこれを実装することにしました。
Githubにソースコードがあります。

さて、今回扱うトレイトは以下の3つです。

trait Born {
    fn born(&mut self) -> bool;
}

trait Die {
    fn die(&mut self) -> bool;
}

trait Animal: Born + Die {
    fn is_alive(&self) -> bool;
}

BornDieを持ち合わせるトレイトオブジェクトを作成するために、Animalオブジェクトを作成しました。

次に、BornDieAnimalのトレイトを実装する構造体DogAnimalを実装しました。

struct Dog {
    alive: Option<bool>,
    name: String,
}

impl Dog {
    fn new(name: String) -> Dog {
        Dog { name, alive: None }
    }
}

impl Animal for Dog {
    fn is_alive(&self) -> bool {
        match self.alive {
            Some(true) => {
                println!("dog[{}] is still alive!", self.name);
                true
            }
            Some(false) => {
                println!("dog[{}] is dead!", self.name);
                false
            }
            None => {
                println!("dog[{}] have not born yet!", self.name);
                false
            }
        }
    }
}

impl Born for Dog {
    fn born(&mut self) -> bool {
        if let Some(s) = self.alive {
            if s {
                println!("dog[{}] have already borned!", self.name);
                return false;
            }
            println!("dog[{}] have already died!", self.name);
            return false;
        }
        self.alive = Some(true);
        println!("dog[{}] borned!", self.name);
        return true;
    }
}

impl Die for Dog {
    fn die(&mut self) -> bool {
        if let Some(s) = self.alive {
            if s {
                self.alive = Some(false);
                println!("dog[{}] died!", self.name);
                return true;
            }
            println!("dog[{}] have already dead", self.name);
            return false;
        }
        println!("dog [{}]have not born yet!", self.name);
        return false;
    }
}

struct Cat {
    alive: Option<bool>,
    name: String,
}

impl Animal for Cat {
    fn is_alive(&self) -> bool {
    //省略
    }
}

impl Cat {
    fn new(name: String) -> Cat {
        Cat { name, alive: None }
    }
}

impl Born for Cat {
    fn born(&mut self) -> bool {
    //省略
    }
}

impl Die for Cat {
    fn die(&mut self) -> bool {
    //省略
    }
}

これらの構造体を一つのVecで扱うことができます。型はVec<Box<dyn Animal>です。

fn main() {
    let mut ani_vec: Vec<Box<dyn Animal>> = Vec::new();

    ani_vec.push(Box::new(Dog::new(String::from("taro"))));
    ani_vec.push(Box::new(Dog::new(String::from("jiro"))));
    ani_vec.push(Box::new(Dog::new(String::from("saburo"))));
    ani_vec.push(Box::new(Cat::new(String::from("nyan"))));
    ani_vec.push(Box::new(Cat::new(String::from("neko"))));
    ani_vec.push(Box::new(Cat::new(String::from("nuko"))));

    for a in ani_vec.iter_mut() {
        a.is_alive();
        a.born();
        a.is_alive();
        a.die();
        a.is_alive();
    }
}

以上です。

2
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
2
0