すぐに使える特徴

標準のRust言語ライブラリには、 派生を使用して無料で宣言できるいくつかの特性があります 。 これらの特性は、独自の構造を宣言するときに役立ちます。さまざまなオープンソースライブラリでよく見られますが、その実装はコンパイラによって生成され、疑問を引き起こす可能性があります。

よく見る:



#[derive(RustcEncodable, RustcDecodable, Clone, Eq, Default)] struct Foo { }
      
      





それが何でどこがわからないのですか



コンパイラー自体は、 #[derive]



を使用して、いくつかの特性の単純な「組み込み」実装を提供できます。 もちろん、より複雑な動作が必要な場合は、これらの同じ特性を手動で実装することもできます。



そのため、「抽出」できる特性のサンプルリストを以下に示します(派生することでそのように変換されます)。





さらに、不安定:





上記の特性はすべて、 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



よりも強く異なります。



PartialOrd PartialOrd



は、比較関数を追加します: ><> =<= 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 OneMul - x * T::one() == x



と組み合わせて使用

ゼロの場合Add - x + T::zero() == x



と組み合わせて使用​​しx + T::zero() == x





これらの特性は、構造体のすべてのメンバーがOne



またはZero



もサポートしてderive



場合、 derive



を使用して宣言できます。



結論 :公共の構造体がいつかベクトル空間で使用される可能性を除外しない場合、たとえばあらゆる種類の代数に参加する場合、構造体にこれらの特性を実装することは理にかなっています。 しかし、これまでのところ、これらの特性について言及すると、コンパイラは誓うだけです...



最も結論的な警告



この特性を実装していないフィールドが、 derive



を通じていくつかのTrait



を実装する構造体に入ると、コードはバラバラになります。 このために特性実装を削除すると、構造を使用している人の間でコードがばらばらになります。 したがって、 黄金律



特性が多いほど、責任は大きくなります。



All Articles