LoginSignup
5
6

More than 5 years have passed since last update.

#tryswiftconf で出てきたポケモンの例題をRustでやってみる

Posted at

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

trait Pokemon

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

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

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

  • protocoltrait
  • typealiastype
  • funcfn
  • 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 ⚡️")
    }
}

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

&mut Pokemon<PokemonType=Thunder>

Swiftではclass AnyPokemon<PokemonType>: 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);
}
5
6
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
5
6