#[derive(RustcEncodable, RustcDecodable, Clone, Eq, Default)] struct Foo { }
#[derive]
. Of course, these same traits can be implemented manually if more complex behavior is required.&T
hash{:?}
Formatterstd
. In addition, you can find self-made libraries, where there are also traits that are extracted using #[derive]
. An example is the commonly used RustcEncodable / RustcDecodable . You can implement derive
support for your traits with the help of very tricky macros (which most likely will not work in half the cases :)), but this is beyond the scope of our article.foo == bar
or not. But why on such a simple matter two different treit? And the answer is:PartialEq
does not guarantee equality with itself: not the fact that a == a
use std::f32; fn main() { let a = f32::NAN; let b = f32::NAN; println!("{}", a == b); }
fn foobar<T>(param: T) { ... }
T
? If complete equivalence is fundamental to us, and without it, the function will collapse, we write this: fn foo<T: Eq>(param: T) { ... }
f32
cannot be passed to this function: for example, as we saw earlier, it only supports PartialEq
. fn bar<T: PartialEq>(param: T) { ... }
Eq
is one more limit than PartialEq
, and does not differ from it in any way in the “less” limiting direction, the following important conclusion follows:All structures that implementEq
must also implementPartialEq
#[derive(Eq)]
entry is not valid, if PartialEq
is not defined on the structure, it PartialEq
error! #[derive(Eq, PartialEq)] enum Foo { ... }
fn foo<T: Eq>()
from the previous example. According to the semantics in it, T
defined as Eq
, but it turns out that it automatically accepts PartialEq
structures as well.Eq
and PartialEq
is that they cannot be automatically extracted for structures whose at least one element does not implement Eq
and PartialEq
respectively. For example: #[derive(Eq, PartialEq)] struct Foo { bar: f32 }
f32
does not implement Eq
. struct Foo {} #[derive(PartialEq)] struct Bar { foo: Foo }
Foo
does not implement PartialEq
(you can play here )derivable
traits . This we will see nextEq
(where possible) or at least PartialEq
in all public structures - it will be useful! And it will save a person who uses your code from sudden compilation errors when he decides to compare a couple of objects, or allows him to include your structure in his own, realizing comparison, without unnecessary tambourines!Eq
and PartialEq
:PartialOrdPartialOrd
adds comparison functions: > , < , > = , <=PartialOrd
= , And implements loose ordering: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);
TraitOrd
implements "strict" ordering:Ordering
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);
To implementOrd
, the structure must implementEq
.
To implementPartialOrd
, the structure must implementPartialEq
Ord
requires a PartialOrd
implementation, so all comparison operations for such structures will work guaranteed. In general, the next is a complete analogy with Eq
and PartialEq
.Eq
and PartialEq
, we try to implement Ord
or at least PartialOrd
in all structures where it is possible and logical. Accordingly, structures with Eq
will be able to support Ord
, structures with PartialEq
- PartialOrd
.Clone
allows you to createT
from&T
using copy.Copy
allows you to copy rather than " move " the value of a variable during assignment.
To implementCopy
, the structure must implementClone
.
Clone
is essentially a common thing. It is implemented for almost everything that can be assumed. Yes, and copying an object by returning a generated copy of an object from the clone()
function is not such a big problem, no matter how such an object is stored in memory - we create a new object, bring it to the same form, return it.Copy
is another song. In essence, the “copy” here is a one-by-one object transfer byte. And not all structures support this functionality. For example, if the pointer is “stupid”, two pointers will be created, showing the same place, which completely contradicts the “safe” ideology of Rust. Or, for example, objects that store some kind of metadata also cannot be copied byte-by-byte.Copy
makes it easier to deal with the transfer semantics. #[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!
work, the second - no. And the difference is in Copy
.Copy
in general, wherever possible. And it is impossible, in general, to implement it where Drop is implemented. Because, in general, if you need to call a destructor for a structure, it means that it is made in such a way that you cannot copy it with a simple memory replica. Clone
does not seem to interfere anywhere.Hash
is responsible for the possibility of taking hash from the structure. This is a prerequisite for compiling a HashMap or HashSet from such structures.Default
is responsible for the initial (default) values ​​of the newly created structure. Structures that implement Default
are often required in various data libraries. Also, the implementation of this trait is useful for structures that characterize the parameters of a system - there are always default parameters, which are too lazy to write every time :)Debug
is responsible for displaying the structure as a text formatted string. Nowhere is required, except in logging libraries, but it is useful when debugging your own programs.derive
same way from derive
:Hash
,Default
, andDebug
can be implemented in those structures, all of whose members supportHash
,Default
andDebug
respectively.
Hash
can be implemented in all public structures - will not be superfluous. Default
and Debug
- to your taste, but desirable. This will help people using your code not to have problems with incorporating your structures into their own. In short, if it’s not a pity, we’re typing #[derive(...)]
and pouring everything from the bounty there.For One : use in conjunction with Mul -x * T::one() == x
For Zero : use in conjunction with Add -x + T::zero() == x
derive
, if all members of the structures also support One
or Zero
.Trait
through derive
, your code will fall apart. And if, because of this, you remove the implementation of your treit, then the code will fall apart from people using your structures. Therefore, the golden rule :The more traits, the greater the responsibility.
Source: https://habr.com/ru/post/277461/
All Articles