S-JIS[2024-09-16/2024-09-19] 変更履歴

Rust トレイト メモ

Rustのトレイトのメモ。


概要

トレイトは、共通の振る舞い(メソッド)を定義するもの。

構造体列挙型でトレイトを実装する。


トレイトの定義方法

トレイトはtraitブロックで定義する。

〔可視性〕trait トレイト名〔<型引数1, 型引数2, …>〕〔: 継承元トレイト〔<型引数, …>〕〕 {
    fn メソッド名(&self, 引数名1 : 型1, 引数名2 : 型2, …) -> 戻り型;
    〜
}

トレイトは別のトレイトを継承することが出来る。

traitブロックで定義するメソッドは、処理本体を書かない。


トレイトの実装方法

トレイトは(構造体列挙型でメソッドを定義するときと同様に)implブロックで実装する。

impl トレイト名 for 構造体または列挙名 {
    fn メソッド名(&self, 引数名1 : 型1, 引数名2 : 型2, …) -> 戻り型 {
        〜
    }

    〜
}

implブロックでは、trait文で定義されたメソッドを実装する。


Javaでは、インターフェースをクラスで実装する構文は「class クラス名 implements インターフェース名」(実装されるクラス名が左側、インターフェース名が右側)である。
Rustでは「impl トレイト名 for 構造体名」(実装される構造体名が右側でトレイト名が左側)で、Javaとは逆。
Javaに慣れた身ではちょっと混乱する^^;

Javaだと、インターフェースで宣言されたメソッドを実装するときは@Overrideというアノテーションを付けるが、
Rustのトレイトでは、そういった印は付けない。


トレイトの例

trait MyTrait {
    fn method(&self);
}

struct MyStruct {
    value: i32,
}

impl MyTrait for MyStruct {
    fn method(&self) {
        println!("value={}", self.value);
    }
}
    let s = MyStruct { value: 123 };
    s.method();

オーバーロードの例

Rustでは、ひとつのimplブロックの中でメソッドをオーバーロードする(同名のメソッドを複数定義する)ことは出来ない。[2024-09-19]

しかし、異なるトレイト同士では同名のメソッドがあっても不思議は無く、ひとつの構造体列挙型が両方のトレイトを実装することが出来る。
結果として、オーバーロードのようなことが実現できる。

struct MyStruct {
    value: i32,
}

trait MyTrait1 {
    fn method(&self);
}

trait MyTrait2 {
    fn method(&self);
}

impl MyTrait1 for MyStruct {
    fn method(&self){
        println!("impl MyTrait1");
    }
}

impl MyTrait2 for MyStruct {
    fn method(&self){
        println!("impl MyTrait2");
    }
}

このようにして、「引数の型が異なる同名メソッド」も「引数の型が同一で戻り型が異なる同名メソッド」も定義できる。
(Javaの場合、引数の型が同一で戻り型のみが異なる同名メソッドは定義できない)


しかしこの場合、呼び出す側でどちらのメソッドを呼ぶかを決定する必要がある。

    let s = MyStruct { value: 123 };
×  s.method();	// MyTrait1の実装とMyTrait2の実装のどちらのmethodを呼び出せばいいか分からないのでコンパイルエラー

どちらのトレイトなのかを明示して呼び出す。

    let s = MyStruct { value: 123 };
    MyTrait1::method(&s);
    MyTrait2::method(&s);

ジェネリクス付きトレイトを型ごとに実装

また、ジェネリクス付きのトレイトを定義して、メソッドの引数や戻り値の型に型引数を使うようにしておくと、トレイトを実装する側で型引数ごとに個別の実装をすることが出来る。

そのメソッドを呼び出す際に、合致する型のメソッドが自動的に選ばれる。
(合致する型のメソッドが無い場合はコンパイルエラーになる)

引数の型が異なるオーバーロードの例(Fromトレイト)
戻り値の型が異なるオーバーロードの例(Intoトレイト)


Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま