付録C: 導出可能なトレイト
本のいろんな箇所でderive
属性について議論しました。これは構造体や、enum定義に適用できます。
derive
属性は、derive
記法で注釈した型に対して独自の既定の実装でトレイトを実装するコードを生成します。
この付録では、標準ライブラリのderive
と共に使用できる全トレイトの参照を提供します。各節は以下を講義します:
- このトレイトを導出する演算子やメソッドで可能になること
derive
が提供するトレイトの実装がすること- トレイトを実装することが型についてどれほど重要か
- そのトレイトを実装できたりできなかったりする条件
- そのトレイトが必要になる処理の例
derive
属性が提供する以外の異なる振る舞いが欲しいなら、それらを手動で実装する方法の詳細について、
各トレイトの標準ライブラリのドキュメンテーションを調べてください。
標準ライブラリで定義されている残りのトレイトは、derive
で自分の型に実装することはできません。
これらのトレイトには知覚できるほどの既定の振る舞いはないので、自分が達成しようしていることに対して、
道理が通る方法でそれらを実装するのはあなた次第です。
導出できないトレイトの例はDisplay
で、これはエンドユーザ向けのフォーマットを扱います。常に、エンドユーザ向けに型を表示する適切な方法について、
考慮すべきです。型のどの部分をエンドユーザは見ることができるべきでしょうか?どの部分を関係があると考えるでしょうか?
どんな形式のデータがエンドユーザにとって最も関係があるでしょうか?Rustコンパイラには、
この見識がないため、適切な既定動作を提供してくれないのです。
この付録で提供される導出可能なトレイトのリストは、包括的ではありません: ライブラリは、自身のトレイトにderive
を実装でき、
derive
と共に使用できるトレイトのリストが実に限りのないものになってしまうのです。derive
の実装には、
プロシージャルなマクロが関連します。マクロについては、付録Dで講義します。
プログラマ用の出力のDebug
Debug
トレイトにより、フォーマット文字列でのデバッグ整形が可能になり、
{}
プレースホルダー内に:?
を追記することで表します。
Debug
トレイトにより、デバッグ目的で型のインスタンスを出力できるようになるので、あなたや型を使用する他のプログラマが、
プログラムの実行の特定の箇所でインスタンスを調べられます。
Debug
トレイトは、例えば、assert_eq!
マクロを使用する際などに必要になります。
このマクロは、プログラマがどうして2つのインスタンスが等価でなかったのか確認できるように、
等価アサートが失敗したら、引数として与えられたインスタンスの値を出力します。
等価比較のためのPartialEq
とEq
PartialEq
トレイトにより、型のインスタンスを比較して、等価性をチェックでき、==
と!=
演算子の使用を可能にします。
PartialEq
を導出すると、eq
メソッドを実装します。構造体にPartialEq
を導出すると、
全フィールドが等しい時のみ2つのインスタンスは等価になり、いずれかのフィールドが等価でなければ、
インスタンスは等価ではなくなります。enumに導出すると、各列挙子は、自身には等価ですが、他の列挙子には等価ではありません。
PartialEq
トレイトは例えば、assert_eq!
マクロを使用する際に必要になります。
これは、等価性のためにとある型の2つのインスタンスを比較できる必要があります。
Eq
トレイトにはメソッドはありません。その目的は、注釈された型の全値に対して、値が自身と等しいことを通知することです。
Eq
トレイトは、PartialEq
を実装する全ての型がEq
を実装できるわけではないものの、
PartialEq
も実装する型に対してのみ適用できます。これの一例は、浮動小数点数型です:
浮動小数点数の実装により、非数字(NaN
)値の2つのインスタンスはお互いに等価ではないことが宣言されます。
Eq
が必要になる一例が、HashMap<K, V>
のキーで、HashMap<K, V>
が、2つのキーが同じであると判定できます。
順序付き比較のためのPartialOrd
とOrd
PartialOrd
トレイトにより、ソートする目的で型のインスタンスを比較できます。PartialOrd
を実装する型は、
<
、>
、<=
、>=
演算子を使用することができます。PartialEq
も実装する型に対してのみ、
PartialOrd
トレイトを適用できます。
PartialOrd
を導出すると、partial_cmp
メソッドを実装し、これは、与えられた値が順序付けられない時にNone
になるOption<Ordering>
を返します。
その型のほとんどの値は比較できるものの、順序付けできない値の例として、非数字(NaN
)浮動小数点値が挙げられます。
partial_cmp
をあらゆる浮動小数点数とNaN
浮動小数点数で呼び出すと、None
が返るでしょう。
構造体に導出すると、フィールドが構造体定義で現れる順番で各フィールドの値を比較することで2つのインスタンスを比較します。 enumに導出すると、enum定義で先に定義された列挙子が、後に列挙された列挙子よりも小さいと考えられます。
PartialOrd
トレイトが必要になる例には、低い値と高い値で指定される範囲の乱数を生成するrand
クレートのgen_range
メソッドが挙げられます。
Ord
トレイトにより、注釈した型のあらゆる2つの値に対して、合法な順序付けが行えることがわかります。
Ord
トレイトはcmp
メソッドを実装し、これは、常に合法な順序付けが可能なので、Option<Ordering>
ではなく、
Ordering
を返します。PartialOrd
とEq
(Eq
はPartialEq
も必要とします)も実装している型にしか、
Ord
トレイトを適用することはできません。構造体とenumで導出したら、PartialOrd
で、
partial_cmp
の導出した実装と同じようにcmp
は振る舞います。
Ord
が必要になる例は、BTreeSet<T>
に値を格納する時です。
これは、値のソート順に基づいてデータを格納するデータ構造です。
値を複製するClone
とCopy
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
を導出することができます。Clone
も実装する型に対してのみ、
Copy
トレイトを適用することができます。何故なら、Copy
を実装する型には、
Copy
と同じ作業を行う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章の「構造体更新記法で他のインスタンスからインスタンスを生成する」節で議論した構造体更新記法と組み合わせてよく使用されます。
構造体のいくつかのフィールドをカスタマイズし、それから..Default::default()
を使用して、
残りのフィールドに対して既定値をセットし使用することができます。
例えば、Default
トレイトは、Option<T>
インスタンスに対してメソッドunwrap_or_default
を使用する時に必要になります。
Option<T>
がNone
ならば、メソッドunwrap_or_default
は、Option<T>
に格納された型T
に対してDefault::default
の結果を返します。