# Traits in Rust
Traits
In Rust, a trait is an abstraction. It contains behavior that can be implemented for a type. The type implementing the trait can either provide a definition for the funtions defined in the trait or use the basic implementation from the trait. Let's see a code example to see what traits look like:
struct Human {
height: i32,
weight: i32,
}
trait Mammalia {
fn mammary_glands() -> String {};
fn neocortex() -> String {};
fn opposable_thumbs() -> String{};
}
All the mammals in the world share some common traits, these common traits can be defined in an abstraction and each specific animal type can implement the Mammalia trait and give their own definition of each function. The ``impl`` keyword is used to implement a trait in rust.
impl Mammalia for Human {
fn mammary_glands() -> String {
return String::from("Humans, like all mammals, have mammary glands.");
}
fn neocortex() -> String {
return String::from("Human neocortex is half of the volume of the brain.");
}
fn opposable_thumbs() -> String {
return String::from("Humans have opposable thumbs.");
}
}
Derive
Using ``#[derive]`` attribute, you can use the basic implementation provided by the compiler, instead of writing your own implementation.
Derivable Traits
Serialize and Deserialize
Implementing Serialize and Deserialize means it can be converted into any data format in serde. Serde supports a variety of types including primitives, compounds types, std types ``(Option, Result)`, Wrapper types `(Box)`` and many more.
#[derive(Serialize, Deserialize)]
struct Human {
height: i32,
weight: i32,
}
Copy and Clone
By default, the assignment operator "moves" the value instead of copying. By implementing the Copy trait, a bit-wise copy of type will be assigned while using the assignement operator. Both Copy and Clone are used for duplicating object. The main difference is that Clone is explicit (have to call .clone()) where as Copy is implicit (triggers memcpy). Copy trait can only be derived if all the fields in a given type also implement copy trait. Let's see what would happen if we tried to derive copy on a trait which has a non-Copy type.
#[derive(Copy)]
struct Human {
height: i32,
weight: i32,
name: String,
}
$ cargo run
error[E0204]: the trait `Copy` cannot be implemented for this type
--> src\main.rs:4:10
|
4 | #[derive(Copy)]
| ^^^^
...
8 | name: String,
| ------------ this field does not implement `Copy
While we are on the subject of Clone, let's see what the derive macro does under the hood for Clone trait using ``cargo expand`` command.
#[automatically_derived]
impl ::core::clone::Clone for Human {
#[inline]
fn clone(&self) -> Human {
Human {
height: ::core::clone::Clone::clone(&self.height),
weight: ::core::clone::Clone::clone(&self.weight),
}
}
}
When we derive the Clone trait, the default implementation of clone method will create a clone of each field present the type.
Some Other Derivable types
- Default - Provide empty data for all fields of the implementing type.
- Debug - If all fields implement Debug, then ``
{:?}`` can be used to print the type. - Eq and PartialEq -- Implementing Eq and PartialEq overloads ``
==`and`!=`` for the type. - Ord and PartialOrd -- Implementing Ord and PartialOrd overloads ``
,`and`>=`` operators.
In Rust, a trait is an abstraction. It contains behavior that can be implemented for a type. The type implementing the trait can either provide a definition for the funtions defined in the trait or use the basic implementation from the trait.