Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

付録C: 導出可能なトレイト

本書のさまざまな箇所で、derive 属性について説明してきました。これは struct や enum の定義に適用できます。derive 属性は、derive 構文で注釈を付けた型に対して、独自のデフォルト実装を持つトレイトを実装するコードを生成します。

この付録では、derive とともに使用できる標準ライブラリ内のすべてのトレイトのリファレンスを提供します。各セクションでは、次の内容を扱います。

  • このトレイトを導出すると、どの演算子やメソッドが使えるようになるか
  • derive が提供するそのトレイトの実装が何を行うか
  • そのトレイトを実装していることが、その型について何を意味するか
  • そのトレイトを実装してよい条件、または実装してはいけない条件
  • そのトレイトを必要とする操作の例

derive 属性が提供するものとは異なる振る舞いが必要な場合は、各トレイトを手動で実装する方法の詳細について、標準ライブラリのドキュメントを参照してください。

ここに挙げるトレイトは、標準ライブラリで定義されているもののうち、derive を使って自分の型に実装できる唯一のものです。標準ライブラリで定義されているほかのトレイトには妥当なデフォルトの振る舞いがないため、何を実現しようとしているのかに応じて意味のある形で自分で実装する必要があります。

導出できないトレイトの例として Display があります。これはエンドユーザー向けの整形を扱います。型をエンドユーザーにどのように表示するのが適切かは、常に検討すべきです。型のどの部分をエンドユーザーに見せるべきでしょうか。どの部分がユーザーにとって関連があるでしょうか。どのようなデータ形式が最も関連性が高いでしょうか。Rust コンパイラにはこのような洞察がないため、適切なデフォルトの振る舞いを提供できません。

この付録で示す導出可能なトレイトの一覧は、網羅的なものではありません。ライブラリは独自のトレイトに対して derive を実装できるため、derive とともに使えるトレイトの一覧は実際には際限なく広がります。derive の実装には手続きマクロを使用します。これについては、第20章の 「カスタム derive マクロ」 節で扱います。

プログラマ向け出力のための Debug

Debug トレイトは、フォーマット文字列でのデバッグ整形を有効にします。これは {} プレースホルダー内に :? を追加することで指定します。

Debug トレイトを使うと、デバッグ目的である型のインスタンスを出力できるため、その型を使うあなたやほかのプログラマは、プログラム実行中の特定の時点でインスタンスを調べることができます。

たとえば Debug トレイトは、assert_eq! マクロの使用時に必要です。このマクロは、等価性のアサーションが失敗した場合に引数として与えられたインスタンスの値を出力するため、プログラマは 2 つのインスタンスがなぜ等しくなかったのかを確認できます。

等価比較のための PartialEqEq

PartialEq トレイトを使うと、型のインスタンスを比較して等しいかどうかを確認でき、== および != 演算子を使用できるようになります。

PartialEq を導出すると、eq メソッドが実装されます。struct に対して PartialEq を導出した場合、2 つのインスタンスが等しいのは、すべての フィールドが等しい場合に限られ、いずれかの フィールドが等しくなければ、そのインスタンス同士は等しくありません。enum に対して導出した場合、各バリアントは自分自身とは等しく、ほかのバリアントとは等しくありません。

たとえば PartialEq トレイトは、assert_eq! マクロを使う際に必要です。このマクロは、型の 2 つのインスタンスを等しいかどうか比較できる必要があります。

Eq トレイトにはメソッドがありません。その目的は、注釈を付けた型のすべての値について、その値が自分自身と等しいことを示すことです。Eq トレイトは、PartialEq も実装している型にしか適用できませんが、PartialEq を実装しているすべての型が Eq を実装できるわけではありません。この例の 1 つが浮動小数点数型です。浮動小数点数の実装では、非数 (NaN) 値の 2 つのインスタンスは互いに等しくないと定義されています。

Eq が必要になる例として、HashMap<K, V> のキーがあります。これは HashMap<K, V> が 2 つのキーが同じかどうかを判定できるようにするためです。

順序比較のための PartialOrdOrd

PartialOrd トレイトを使うと、ソートのために型のインスタンスを比較できます。PartialOrd を実装した型は、<><=>= 演算子とともに使用できます。PartialOrd トレイトは、PartialEq も実装している型にしか適用できません。

PartialOrd を導出すると、partial_cmp メソッドが実装されます。このメソッドは Option<Ordering> を返し、与えられた値から順序を決定できない場合は None になります。その型のほとんどの値は比較できるとしても、順序を決定できない値の例として NaN 浮動小数点値があります。任意の浮動小数点数と NaN 浮動小数点値に対して partial_cmp を呼び出すと、None が返ります。

struct に対して導出した場合、PartialOrd は struct 定義内でフィールドが現れる順序に従って、各フィールドの値を比較することで 2 つのインスタンスを比較します。enum に対して導出した場合、enum 定義で先に宣言されたバリアントは、後に列挙されたバリアントよりも小さいと見なされます。

たとえば PartialOrd トレイトは、rand クレートの gen_range メソッドで、範囲式によって指定された範囲内のランダムな値を生成する際に必要です。

Ord トレイトを使うと、注釈を付けた型の任意の 2 つの値について、妥当な順序が存在することがわかります。Ord トレイトは cmp メソッドを実装します。このメソッドは Option<Ordering> ではなく Ordering を返します。なぜなら、妥当な順序は常に存在するからです。Ord トレイトは、PartialOrdEq も実装している型にしか適用できません(そして Eq には PartialEq が必要です)。struct と enum に対して導出した場合、cmpPartialOrd における partial_cmp の導出実装と同じように振る舞います。

Ord が必要になる例として、値の並び順に基づいてデータを格納するデータ構造である BTreeSet<T> に値を格納する場合があります。

値を複製するための CloneCopy

Clone トレイトを使うと、値のディープコピーを明示的に作成でき、その複製処理には任意のコードの実行やヒープデータのコピーが含まれる場合があります。Clone の詳細については、第4章の Clone と相互作用する変数とデータ」 節を参照してください。

Clone を導出すると、clone メソッドが実装されます。このメソッドは、型全体に対して実装されると、型を構成する各部分に対して clone を呼び出します。つまり、Clone を導出するには、その型のすべてのフィールドまたは値も Clone を実装していなければなりません。

Clone が必要になる例として、スライスに対して to_vec メソッドを呼び出す場合があります。スライスは含んでいる型のインスタンスを所有していませんが、to_vec が返すベクタはそれらのインスタンスを所有する必要があるため、to_vec は各要素に対して clone を呼び出します。したがって、スライスに格納されている型は Clone を実装していなければなりません。

Copy トレイトを使うと、スタックに格納されているビットをコピーするだけで値を複製できます。任意のコードは必要ありません。Copy の詳細については、第4章の 「スタックのみに置かれるデータ: Copy 節を参照してください。 Copy トレイトは、プログラマーがそれらのメソッドをオーバーロードして、任意のコードが実行されていないという前提を破ることを防ぐために、いかなるメソッドも定義していません。そうすることで、すべてのプログラマーは、値のコピーが非常に高速であると想定できます。

構成要素がすべて Copy を実装している任意の型に対して、Copy を導出できます。Copy を実装する型は、Copy と同じ処理を行う自明な Clone 実装を持つため、Clone も実装していなければなりません。

Copy トレイトが必要になることはまれです。Copy を実装する型では最適化を利用できるため、clone を呼び出す必要がなくなり、コードをより簡潔にできます。

Copy で可能なことはすべて Clone でも実現できますが、コードが遅くなったり、ところどころで clone を使わなければならなくなったりする可能性があります。

値を固定サイズの値に対応付けるための Hash

Hash トレイトを使うと、任意のサイズの型のインスタンスを受け取り、ハッシュ関数を使ってそのインスタンスを固定サイズの値に対応付けることができます。Hash を導出すると、hash メソッドが実装されます。導出された hash メソッドの実装は、その型の各部分に対して hash を呼び出した結果を組み合わせるため、Hash を導出するには、すべてのフィールドまたは値も Hash を実装していなければなりません。

Hash が必要になる例としては、データを効率的に格納するために、HashMap<K, V> にキーを格納する場合があります。

デフォルト値のための Default

Default トレイトを使うと、型のデフォルト値を作成できます。Default を導出すると、default 関数が実装されます。導出された default 関数の実装は、その型の各部分に対して default 関数を呼び出すため、Default を導出するには、その型のすべてのフィールドまたは値も Default を実装していなければなりません。

Default::default 関数は、第5章の 「Struct Update Syntax を使って他のインスタンスからインスタンスを生成する」 節で説明した構造体更新構文と組み合わせてよく使われます。構造体のいくつかのフィールドをカスタマイズし、残りのフィールドには ..Default::default() を使ってデフォルト値を設定して利用できます。

たとえば、Option<T> のインスタンスに対して unwrap_or_default メソッドを使う場合には、Default トレイトが必要です。Option<T>None の場合、unwrap_or_default メソッドは Option<T> に格納されている型 T に対する Default::default の結果を返します。