#[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 implementEqmust 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 :PartialOrdPartialOrdadds 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); TraitOrdimplements "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 .Cloneallows you to createTfrom&Tusing copy.Copyallows 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, andDebugcan be implemented in those structures, all of whose members supportHash,DefaultandDebugrespectively.
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