The Rust standard library is packed with traits, which are used all over the place. For example, there are traits for which the compiler is capable of providing a basic implementation with a #[derive] attribute, as we saw in the section on Traits:
- Comparing instances: The Eq and PartialEq trait
- Ordering instances: The Ord and PartialOrd trait
- Creating an empty instance: The Default trait
- To create a zero instance of a numeric data type: The Zero trait
The next chapter shows an example of how to implement the following three traits:
- Formatting a value using {:?}: The Debug trait, defining an fmt method
- Copy an instance: The Copy trait
- Create a duplicate instance: The Clone trait
- Computing a hash: The Hash trait
- Adding instances: The Add trait, defining an add method. The + operator is just a nice way to use add: n + m is the same as n.add(m). So if we implement the Add trait, we can use the + operator, this is called operator overloading.
- The Add trait has the following signature:
pub trait Add<RHS = Self> { type Output; fn add(self, rhs: RHS) -> Self::Output; }
So the add method and the trait item Output must both be implemented. The code impl_add.rs shows an implementation of the Add trait:
struct AType { value: i32, } impl AType { fn new(value: i32) -> AType { AType { value: value } } } impl Add for AType { type Output = AType; fn add(self, other: AType) -> AType { AType { value: self.value + other.value } } } fn main() { let at1 = AType{ value: 7 }; let at2 = AType{ value: 42 }; let at3 = at1.add(at2); println!("{:?}", at3); let at4 = AType{ value: 2 }; let at5 = AType{ value: 108 }; let at6 = at4 + at5; println!("{:?}", at6); }
- This prints the following output:
AType { value: 49 } AType { value: 110 }
- Freeing resources of an object (when it goes out of scope): The Drop trait, in other words the object has a destructor. Freeing resources is taken care of automatically by the compiler, but by implementing Drop you can do additional things.
In the section Iterators we described how an iterator works and used it on ranges and arrays. In fact an iterator is also defined as a trait in Rust in std::iter::Iterator. From the docs for iterator (see http://doc.rust-lang.org/std/iter/trait.Iterator.html) we see that we only need to define the next() method, which advances the iterator to return the next value as an Option type. When the next() method is implemented for the type of your object, we can then use a for in loop to iterate over the object.