背景、やること

最近Rustをいじっていた際トレイトという概念で扱う範囲が広くよくわからなくなってきたため、その役割についてとりあえず分かったことをまとめる。

トレイトの役割

トレイトの役割の1つとして、「Javaのインターフェース(interface)のような機能です。共通の振る舞いを取り出して名前付けしたものをいいます。」とあった(実践Rustプログラミング入門より)。

トレイトの意味を知るために、ある動物の寿命と学術名を表示するプログラムについてトレイトを使用しない場合と使用する場合(参考書記載)のプログラムを用意し、比較する。

トレイトを使わない場合

プログラムは以下のようになる。動作としてはほぼ同じ(life_span, scientific_nameを表示する)だが、動物の数だけanimal_show_data()関数の実装をする必要が出てくる。

fn main() {
    let dog = Dog{};
    let cat = Cat{};
    show_animal_data_dog(dog);
    show_animal_data_cat(cat);
}

//犬の構造体。
struct Dog;
impl Dog {
    //寿命を返す。
    fn lifespan(&self) -> u32 {
        13
    }
    //学名を返す。
    fn scientific_name(&self) -> String {
        "Canis lups familiaris".to_string()
    }
}

//猫の構造体
struct Cat;
impl Cat {
    fn lifespan(&self) -> u32 {
        16
    }

    fn scientific_name(&self) -> String {
        "Felis catus".to_string()
    }
}

//犬のデータを表示する関数
fn show_animal_data_dog(d: Dog) {
    println!("life_span: {}", d.lifespan());
    println!("scientific_name: {}", d.scientific_name());
}

//猫のデータを表示する関数
fn show_animal_data_cat(c: Cat) {
    println!("life_span: {}", c.lifespan());
    println!("scientific_name: {}", c.scientific_name());
}

//...以下動物の数だけ関数を宣言する。

トレイトを使う場合

以下のプログラムは書籍において紹介されていたもので、Animal型というトレイトと、その中でAnimal型を実装した型に共通する振る舞いを実装している。
具体的にはAnimal型を実装した型として構造体Dog, Catが存在しており、それらに共通する振る舞い(lifespan, scientific_name)をAnimalトレイトで実装している。
このトレイトにより、Animal型を実装した型は全てshow_animal_data()関数を通して動物のデータを表示可能となる。

fn main() {
    let dog = Dog{};
    let cat = Cat{};
    show_animal_data(dog);
    show_animal_data(cat);
}

trait Animal {
    fn lifespan(&self) -> u32;
    fn scientific_name(&self) -> String;
}

struct Dog;
impl Animal for Dog {
    fn lifespan(&self) -> u32 {
        13
    }

    fn scientific_name(&self) -> String {
        "Canis lups familiaris".to_string()
    }
}

struct Cat;
impl Animal for Cat {
    fn lifespan(&self) -> u32 {
        16
    }

    fn scientific_name(&self) -> String {
        "Felis catus".to_string()
    }
}

//Animal型をもつ動物のデータを表示する関数。
fn show_animal_data<T: Animal>(animal: T) {
    println!("Lifespan: {} years", animal.lifespan());
    println!("Scientific Name: {}", animal.scientific_name());
}

まとめ

トレイトを使って同様の出力を行う部分を構造体ごとに実装する必要がなくなることが分かった。トレイト自体の役割は他にもいろいろあるため時間があるときにまとめていきたい。

bannerAds