Home » Rust Trait

Rust Trait

  • Rust trait is a feature of a Rust language that describes the functionality of each type that it can provide.
  • A trait is similar to the feature of an interface defined in other languages.
  • A trait is a way to group the method signatures to define a set of behaviors.
  • A Trait is defined by using the trait keyword.

The Syntax of the trait:

In the above case, we declare the trait followed by the trait name. Inside the curly brackets, method signature is declared to describe the behavior of a type that implements the trait.

Let’s see a simple example:

Output:

Area of a triangle is 91.35  

In the above example, trait named as HasArea is declared which contains the declaration of area() function. HasArea is implemented on the type Triangle. An area() function is simply called by using the instance of the structure, i.e., a.area().

Trait as Arguments

Traits can also be used as arguments of many different types.

The above example implements the HasArea trait, and it contains the definition of the area() function. We can define the calculate_area() function that calls the area() function, and the area() function is called using the instance of the type that implements the HasArea trait.

Let’s look at the syntax:

Trait bounds on Generic functions

Traits are useful because they describe the behavior of different methods. But, Generic functions does not follow this constraint. Let’s understand this through a simple scenario:

In the above case, Rust compiler throws an “error that no method named found of type T”. If we bound the trait to the generic T, then the following error can be overcome:

In the above case, <T: HasArea> means “T can be of any type that implements HasArea trait”. Rust compiler got to know that any type that implements the HasArea trait will have an area() function.

Let’s see a simple example:

Output:

Area is : 91.35  Area is : 20.25  

In the above example, calculate_area() function is generic over “T”.

Rules for implementing traits

There are two limitations to implementing the trait:

  • If the trait is not defined in your scope, then it cannot be implemented on any data type.

Let’s see a simple example:

Output:

error : no method named 'write' found.             let result = f.write(str);  

In the above case, Rust compiler throws an error, i.e., “no method named ‘write’ found” as use::std::fs::File; namespace does not contain the write() method. Therefore, we need to use the Write trait to remove the compilation error.

  • The trait which we are implementing must be defined by us. For example: If we define the HasArea trait, then we can implement this trait for the type i32. However, we could not implement the toString trait defined by the Rust for the type i32 as both the type and trait are not defined in our crate.

Multiple trait bounds

  • Using ‘+’ operator.

If we want to bound the multiple traits, we use the + operator.

Let’s see a simple example:

Output:

Debug: ' "tutoraspire"'  Display: ' tutoraspire'  

In the above example, Display and Debug traits are bounded to the type ‘T’ by using the ‘+’ operator.

  • Using ‘where’ clause.
    • A bound can be written using a ‘where’ clause which appears just before the opening bracket ‘{‘.
    • A ‘where’ clause can also be applied to the arbitrary types.
    • When ‘where’ clause is used, then it makes the syntax more expressive than the normal syntax.

Let’s look:

When ‘where’ is used in the above case:

In the above cases, the second case where we have used the ‘where’ clause makes the program more expressive and readable.

Let’s see a simple example:

Output:

Perimeter of a square is 24.8  Perimeter of a rectangle is 17.6  

Default methods

A default method can be added to the trait definition if the definition of a method is already known.

Let’s look:

In the above case, the default behavior is added to the trait definition. We can also override the default behavior. Let’ look at this scenario through an example:

Output:

Value of a is : 5  Value of b is : 7  

In the above example, the behavior of b() function is defined in the trait is overridden. Therefore, we can conclude that we can override the method which is defined in the trait.

Inheritance

The trait which is derived from another trait is known as inheritance. Sometimes, it becomes necessary to implement the trait that requires implementing another trait. If we want to derive ‘B’ trait from ‘A’ trait, then it looks like:

Let’s see a simple example:

Output:

tutoraspire tutorial  

In the above example, our program is implementing the ‘B’ trait. Therefore, it also requires to implement the ‘A’ trait. If our program does not implement the ‘A’ trait, then the Rust compiler throws an error.


Next Topic#

You may also like