よく見る:
#[derive(RustcEncodable, RustcDecodable, Clone, Eq, Default)] struct Foo { }
それが何でどこがわからないのですか
コンパイラー自体は、
#[derive]
を使用して、いくつかの特性の単純な「組み込み」実装を提供できます。 もちろん、より複雑な動作が必要な場合は、これらの同じ特性を手動で実装することもできます。
そのため、「抽出」できる特性のサンプルリストを以下に示します(派生することでそのように変換されます)。
- [↓]比較特性: Eq 、 PartialEq 、 Ord 、 PartialOrd
- [↓] クローンとコピーは、 クローンとコピーの原因となる特性です。
- [↓] ハッシュ -ハッシュ
&T
計算用 - [↓] デフォルト -「デフォルト値」を設定できます
- [↓] デバッグ -
{:?}
:?{:?}
フォーマッターを使用するときに構造値を出力するための形式を定義します
さらに、不安定:
上記の特性はすべて、
std
標準ライブラリにあります。 さらに、自分の手で作成されたライブラリを見つけることができます。
#[derive]
を使用して抽出された特性もあります。 例は、一般的に使用されるRustcEncodable / RustcDecodableです。 非常にトリッキーなマクロを使用して、形質の
derive
サポートを実装できます(これは、ほとんどの場合、半分のケースでは機能しません:))が、これは私たちの記事の範囲外です。
上記の特性を考慮してください
特性を比較する
それらを使用すると、構造に等価関係のモデルを配置できます。 その後、構造を比較、整理、構造化、整理できます。 それらをより詳細に検討しましょう。
EqおよびPartialEq
foo == bar
かどうかについては、2つの類似した特性が原因です。 しかし、なぜこのような単純な2つの異なる特性があるのでしょうか? そして、解決策はこれです:
PartialEq
はそれ自体との同等性を保証しません:a == aという事実ではありません
これはどのようにできますか? たとえば、数値NaN!= NaN
use std::f32; fn main() { let a = f32::NAN; let b = f32::NAN; println!("{}", a == b); }
falseを返します
ある種の比較機能を備えたライブラリの作成者を想像してください。
fn foobar<T>(param: T) { ... }
T
制限を選択する方法は? 完全な等価性が基本であり、それがなければ関数がばらばらになる場合、次のように記述します。
fn foo<T: Eq>(param: T) { ... }
ただし、この場合、たとえば、この関数に
f32
を渡すことはできません。前述したように、
PartialEq
のみをサポートしてい
PartialEq
。
そのような制限が重要ではなく、関数がパラメーターに対して安定している場合、セマンティクスは次のようになります。
fn bar<T: PartialEq>(param: T) { ... }
制限の少ないこの機能( ここでプレイできます )
Eq
は
PartialEq
よりも
PartialEq
1つの制限であり、制限の点で「より小さい」という点で
Eq
と変わらないという事実は、次の重要な結論を暗示しています。
Eq
を実装するすべての構造は、PartialEq
も実装する必要がありますPartialEq
つまり、
PartialEq
が構造で定義されていない場合、エントリ
#[derive(Eq)]
有能ではありません- エラーが
PartialEq
されます!
これを回避する最も簡単な方法:
#[derive(Eq, PartialEq)] enum Foo { ... }
前の例の関数
fn foo<T: Eq>()
を思い出してください。 そのセマンティクスにより、
T
Eq
として定義されますが、
PartialEq
構造を自動的に受け入れることが
PartialEq
ます。
Eq
と
PartialEq
1つのプロパティは、少なくとも1つの要素がそれぞれ
Eq
と
PartialEq
実装していない構造体に対して自動的に抽出できないことです。 例:
#[derive(Eq, PartialEq)] struct Foo { bar: f32 }
すでにわかっているように、
f32
は
Eq
実装していないため、エラーが発生します。
同様に、次のコードは機能しません。
struct Foo {} #[derive(PartialEq)] struct Bar { foo: Foo }
Foo
構造は
PartialEq
実装していないため( ここでプレイできます )
ただし、 この動作はすべての派生
derivable
な特性に対応します 。 これはさらに見る
結論:コードを書くときは、
Eq
(可能な場合)を実装するか、少なくともすべてのパブリック構造体に少なくとも
PartialEq
を実装するのが賢明です。 そして、いくつかのオブジェクトを比較することを決めたとき、突然のコンパイルエラーからコードを使用している人を救うか、自分の構造を自分の構造に含めて、タンバリンなしで比較を実装することができます!
OrdおよびPartialOrd
繰り返しますが、2つの同様の特性がありますが、今回のみがより少ない責任を負います。 ただし、それらは
Eq
および
PartialEq
よりも強く異なります。
PartialOrdPartialOrd
は、比較関数を追加します: > 、 < 、 > = 、 <=PartialOrd
= 、 =そして、厳密な順序付けを実装します:Option
<Ordering>
use std::cmp::Ordering; let result = 1.0.partial_cmp(&2.0); assert_eq!(result, Some(Ordering::Less)); let result = 1.0.partial_cmp(&1.0); assert_eq!(result, Some(Ordering::Equal)); let result = 2.0.partial_cmp(&1.0); assert_eq!(result, Some(Ordering::Greater)); let result = std::f64::NAN.partial_cmp(&1.0); assert_eq!(result, None);
特性Ord
は、「厳密な」順序付けを実装します。
use std::cmp::Ordering; assert_eq!(5.cmp(&10), Ordering::Less); assert_eq!(10.cmp(&5), Ordering::Greater); assert_eq!(5.cmp(&5), Ordering::Equal);
これらの特性の依存関係を見ると、すべてが明らかになります。
Ord
を実装するには、フレームワークがEq
実装する必要があります。
PartialOrd
を実装PartialOrd
には、構造体にPartialOrd
を実装する必要があります
確かに、構造がそれ自体に等しくないことが判明した場合、「より大きい」または「より大きいまたは等しい」値が2番目の値に相対的であるかどうかを確実に言うことは可能ですか。
また、
Ord
では
PartialOrd
の実装が
PartialOrd
であるため、このような構造のすべての比較演算は保証された状態で機能します。 一般的に、さらに
PartialEq
および
PartialEq
との完全なアナロジー。
ここでこの経済のすべてで遊ぶことができます 。
結論 :
Eq
および
PartialEq
との類推により、
Ord
または少なくとも
PartialOrd
を、これが可能かつ論理的なすべての構造に実装しようとします。 したがって、
Eq
を持つ構造は
Ord
、
PartialEq
-
PartialOrd
持つ構造をサポートできます。
クローンとコピー
ロシア語でさえ、「クローニング」と「コピー」という言葉は区別がつかない同義語です。 しかし、Rustでは、これらは2つの異なるものです。 今、私たちはそれらに対処します
Clone
使用すると、コピーを使用してT
からT
を作成できます。
Copy
は、割り当て時に変数の値を「 移動 」するのではなく、Copy
することができます。
要するに、これらの特性は一般にさまざまなことに責任があり、それらは次の事実によってのみ関連しています:
Copy
を実装するには、構造にClone
実装する必要があります。
Clone
は基本的に一般的なものです。 想定されるほとんどすべてのものに実装されています。 そして、生成されたオブジェクトのコピーを
clone()
関数から返すことでオブジェクトをコピーすることは、そのようなオブジェクトがメモリに保存されなかったほど大きな問題ではありません-新しいオブジェクトを作成し、同じフォームに持って帰ります。
Copy
は別の歌です。 基本的に、ここでの「コピー」とは、オブジェクトを1バイトずつ転送することです。 また、すべての構造がこの機能をサポートしているわけではありません。 たとえば、ポインターの「ダム」コピーでは、同じ場所を指す2つのポインターが作成されますが、これは「安全な」Rustイデオロギーと完全に矛盾しています。 または、たとえば、ある種のメタデータを格納するオブジェクトも、バイト単位でコピーできません。
ただし、
Copy
すると、転送のセマンティクスを簡単に処理できます。
#[derive(Debug, Copy, Clone)] struct Foo; #[derive(Debug, Clone)] struct Bar; fn main(){ let foo = Foo; let bar = foo; println!("foo is {:?} and bar is {:?}", foo, bar); let foo = Bar; let bar = foo; println!("foo is {:?} and bar is {:?}", foo, bar); }
最初の
println!
動作します、2番目-いいえ。 そして、違いは
Copy
です。
ここで遊ぶことができます 。
結論 :公式ドックでアドバイスされているように、可能な限り一般的に
Copy
を実装します。 ただし、一般的な場合、 Dropが実装されている場合は実装できません。 一般的なケースでは、構造体のデストラクタを呼び出す必要がある場合、単純なメモリキャストではコピーできないようにデストラクタが作成されるためです。
Clone
はどこにも傷をつけないようです。
ハッシュ、デフォルト、およびデバッグ
特性
Hash
は、構造からハッシュを取得する可能性を担当します。 これは、このような構造HashMapまたはHashSetを構成するために必要な条件です。
Default
特性は、新しく作成された構造の初期(デフォルト)値を担当します。 多くの場合、
Default
を実装する構造がさまざまなデータライブラリに必要です。 この特性の実装は、システムのパラメーターを特徴付ける構造にも役立ちます-毎回書き込むのが面倒なデフォルトのパラメーターが常にあります:)
Debug
トレイトは、構造をテキスト形式の文字列として表示します。 ロギングライブラリを除き、どこにも必要ありませんが、独自のプログラムをデバッグするときに役立ちます。
特性はそれ自体が異なり、それらは
derive
と同じように機能するため、それらを組み合わせます:
Hash
、Default
、およびDebug
は、すべてのメンバーがそれぞれHash
、Default
およびDebug
サポートする構造に実装できます。
そして、これは必要なだけでなく、十分です。 余分な手間、特性、その他はありません。 ここでこの農場で遊ぶことができます 。
結論 :
Hash
はすべてのパブリック構造に実装できます-不要ではありません。
Default
と
Debug
-あなたの好みに合わせて、しかしできれば。 これにより、コードを使用している人が構造を自分の構造に組み込むときに問題が発生しないようになります。 簡単に言えば、それが残念でない場合は、
#[derive(...)]
と入力し、そこから賞金のすべてを注ぎます。
1と0
この記事の執筆時点(Rust 1.6 Stable)では、これらの特性は不安定であると宣言されています。 ただし、将来的には、加算( Add )および乗算( Mul )の操作と組み合わせて使用すると、任意のデータ構造のベクトル空間を定義できる可能性が高くなります。 これらの特性を実装するには、構造に次のプロパティが必要です。
For One : Mul -x * T::one() == x
と組み合わせて使用
ゼロの場合 : Add -x + T::zero() == x
と組み合わせて使用しx + T::zero() == x
これらの特性は、構造体のすべてのメンバーが
One
または
Zero
もサポートして
derive
場合、
derive
を使用して宣言できます。
結論 :公共の構造体がいつかベクトル空間で使用される可能性を除外しない場合、たとえばあらゆる種類の代数に参加する場合、構造体にこれらの特性を実装することは理にかなっています。 しかし、これまでのところ、これらの特性について言及すると、コンパイラは誓うだけです...
最も結論的な警告
この特性を実装していないフィールドが、
derive
を通じていくつかの
Trait
を実装する構造体に入ると、コードはバラバラになります。 このために特性の実装を削除すると、構造を使用している人の間でコードがばらばらになります。 したがって、 黄金律 :
特性が多いほど、責任は大きくなります。