はじめに
複数のトレイト境界を持つトレイトオブジェクトを取り扱ったので忘備録として投稿致します。
ソースコードは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();
}
}
解決
そこで、トレイトA
、B
を合成したトレイト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();
}
}
結果
既存のトレイトA
、B
を合成したトレイト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;
}
Born
とDie
を持ち合わせるトレイトオブジェクトを作成するために、Animal
オブジェクトを作成しました。
次に、Born
、Die
、Animal
のトレイトを実装する構造体Dog
、Animal
を実装しました。
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();
}
}
以上です。