型消去の話で出てきたポケモンの例題を理解する #tryswiftconfで出てきた例をRustに移植してみます。

    • Gistでコードを見る

 

    Rust Playgroundで実行する

trait Pokemon

Swiftのprotocolに相当するものとして、Rustにはtraitがあります。

trait Pokemon {
    type PokemonType;
    fn attack(&mut self, move_: Self::PokemonType);
}

関連型 (Associated Types) が使える点を含めて、Swiftの例とほとんど一緒ですね! ですが、以下の相違点があります。

protocol→trait

typealias→type

func→fn

Void→() ですが、関数の返り値の型では省略できます。
Rustでは行末にセミコロンが要ります。
Rustではレシーバー&mut selfを明示的に書きます。
Rustではmoveが予約語なので、代わりにmove_としています。
RustではPokemonTypeの名前空間を指定するためにキーワードSelfが要ります。

struct Pikachu

重要な点として、Rustにはクラスが存在せず、データ定義とメソッド実装は必ず別に書かれます。

struct Thunder;

struct Pikachu;

impl Pokemon for Pikachu {
    type PokemonType = Thunder;
    fn attack(&mut self, _move_: Thunder) {
        println!("Pikachu attacks ⚡️")
    }
}

struct Thunder;のように書くと、フィールドを持たない構造体が定義できます。このような構造体をunit-like structsと呼びます。(unitは()の呼び名です。)

impl Pokemon for Pikachuの部分で、PokemonトレイトをPikachu型に実装しています。このPikachu型の実装を使うには、次のように書けばいいです。

let mut pikachu = Pikachu;
pikachu.attack(Thunder);

同様にRaichuも定義してしまいます。

struct Raichu;

impl Pokemon for Raichu {
    type PokemonType = Thunder;
    fn attack(&mut self, _move_: Thunder) {
        println!("Raichu attacks ⚡️")
    }
}

PikachuとRaichuはそれぞれ別の型のデータですが、同じ型のデータとして取り扱うことはできるのでしょうか?

&mut Pokemon<PokemonType=Thunder>

Swiftではclass AnyPokemon: Pokemonを定義することで問題を解決していましたが、Rustではその必要がありません。関連型がある場合にもtrait objectが使えます。ただし、関連型がある場合のtrait objectの型は&mut Pokemon<PokemonType=Thunder>のような記法で、関連型を具体的に指定します。

let electric_pokemon: Vec<&mut Pokemon<PokemonType=Thunder>>
            = vec![&mut pikachu, &mut raichu];
for mut pokemon in electric_pokemon {
    pokemon.attack(Thunder);
}
广告
将在 10 秒后关闭
bannerAds