Get Started with Rust: Traits
Tags: rust
Published: 2022-11-08 19:15:29

---
title: Get Started with Rust: Traits
published: true
date: 2022-11-07 00:00:00 UTC
tags: rust
canonical_url: https://serokell.io/blog/rust-traits
---

# Get Started with Rust: Traits

A trait is a basic language concept for defining shared behavior on types. Traits describe an interface that types can implement.

Rust traits are a sibling of [Scala traits](https://docs.scala-lang.org/tour/traits.html) and [Haskell type classes](https://serokell.io/blog/haskell-typeclasses), as well as a cousin of C++ and Java interfaces.

This article will show you how to use traits in Rust. After reading it, you’ll be able to answer these questions:

- What is a trait?
- Why do we use traits in Rust?
- How to implement and define traits in Rust?
- What does it mean to derive a trait, and when can we do it?

We expect that you are somewhat familiar with [structs](https://serokell.io/blog/structs-in-rust) and [enums](https://serokell.io/blog/enums-and-pattern-matching) in Rust.

## What is a trait?

Traits allow us to define interfaces or shared behaviors on types. To implement a trait for a type, we need to implement methods of that trait.

For example, let’s look at the simplified version of the [`PartialEq`](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) trait, which allows us to define equality for user-defined types:

```
trait PartialEq {
    fn eq(&self, other: &Self) -> bool;
}

```

For a type to implement `PartialEq`, it needs to implement its method: `eq`. Implementing it allows us to write `x == y` and `x != y` for this type.

## Traits are everywhere

You don’t need to dig too deep to find traits in Rust. Let’s look at a couple of examples.

You might be familiar with `for` loops:

```
for i in [1, 2, 3] {
    println!("{i}");
}
// Prints: 
// 1
// 2
// 3

```

Rust’s `for` loop syntax is actually syntactic sugar for [iterators](https://doc.rust-lang.org/std/iter/trait.Iterator.html), which are responsible for the logic of iterating over some items.

There’s a trait in the standard library for converting something into an iterator called [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html). This is usually implemented for types that describe a collection. This includes Vectors, HashMaps, and Options.

Let’s look at another example.

Imagine that we’ve defined a simple `Book` struct and created an instance:

```
struct Book {
    title: String,
    author: String
}

let my_book = Book {
    title: String::from("Case of the Stuffed Goose"),
    author: String::from("Theo Witty"),
};

```

What can we do with it? Can we print a book?

```
println!("My book: {my_book}");

```

No. The code doesn’t compile:

```
error: `Book` doesn't implement `std::fmt::Display`
help: the trait `std::fmt::Display` is not implemented for `Book`

```

And the compiler gives us a hint. `std::fmt::Display` is a trait that we must implement for our `Book` struct if we want to print it out.

What else can we try? Can we compare two books?

```
let your_book = Book {
    title: String::from("Case of the Shrieking Bacon"),
    author: String::from("Casey Fennimore"),
};

if my_book == your_book {
	...
}

```

Also no. We get a slightly different, but similar compilation error:

```
error: binary operation `==` cannot be applied to type `Book`
   |
   | my_book == your_book
   | ------- ^^ --------- Book
   | |
   | Book
   |
note: an implementation of `PartialEq<_>` might be missing for `Book`
help: consider annotating `Book` with `#[derive(PartialEq)]`
   |
   | #[derive(PartialEq)]
   |

```

We must implement `PartialEq` for our `Book` struct if we want to check for equality using the `==` operator.

We’ve got a hint that we should provide an implementation for these traits. Let’s do that next.

## How to implement a trait?

### How to implement a trait manually?

We should keep in mind that a trait defines an interface. In order for a type to implement a trait, it must provide definitions of all the required methods.

Let’s implement the `PartialEq` trait for `Book`. The only method we have to define is `eq`.

```
impl PartialEq for Book {
    fn eq(&self, other: &Self) -> bool {
        self.title == other.title && self.author == other.author
    }
}

```

These are a lot of new words at once, so let’s take a closer look. The first line defines the trait implementation:

```
//[1][2] [3] [4]
impl PartialEq for Book {

// [1]: `impl` keyword.
// [2]: The trait name (in this case, PartialEq).
// [3]: `for` keyword.
// [4]: The type name (in this case, Book).

```

Then we have to define the method:

```
// [1][2] [3]
    fn eq(&self, other: &Self) -> bool {
// [4]
        self.title == other.title && self.author == other.author
    }
}

// [1]: Trait instance method has
// [2]: the **&self** parameter as the first parameter;
// [3]: any extra parameters must come after.
// [4]: Implementation details.

```

**Note:** `eq` is an instance method – a trait method that requires an instance of the implementing type via the `&self` argument. Traits can also define static methods that don’t require an instance and do not have `&self` as their first parameter, but we aren’t going to cover them here.

The `self` is an instance of the implementing type that gives us access to its internals. For example, we can get our struct fields with `self.title` and `self.author`.

The actual implementation of `eq` is quite primitive. We compare each field between the structs and make sure that all of them are equal:

```
    self.title == other.title && self.author == other.author

```

* * *

💡 **`&self` is syntactic sugar for `self: &Self`.**

The `Self` keyword is only available within type definitions, trait definitions, and `impl` blocks. In trait definitions, `Self` stands for the implementing type. For more information, see [The Rust Reference](https://doc.rust-lang.org/reference/paths.html#self-1).

* * *

We can compare our books now:

```
if my_book == your_book {
    println!("We have the same book!");
} else {
    println!("We have different books.");
}
// Prints: 
// We have different books.

```

* * *

💡 **Which common traits should my types implement?**

There is no list of required traits. If you are writing an application, implement traits that you need as the need arises. If you are writing a library that will be used by others, it’s trickier. On the one hand, not adding a trait can limit your library users. On the other hand, removing a “wrong” trait is not backward-compatible. You can find more information on interoperability in the [Rust API Guidelines](https://rust-lang-nursery.github.io/api-guidelines/interoperability.html).

* * *

Defining traits can be tedious and error-prone. That’s why it’s common to ask the compiler for help. The Rust compiler can provide an implementation for some of the traits via the derive mechanism.

### Deriving a trait

Let’s drop the `PartialEq` implementation for the `Book` and rewind back to the compiler error:

```
error: binary operation `==` cannot be applied to type `Book`
   |
   | my_book == your_book
   | ------- ^^ --------- Book
   | |
   | Book
   |
note: an implementation of `PartialEq<_>` might be missing for `Book`
help: consider annotating `Book` with `#[derive(PartialEq)]`
   |
   | #[derive(PartialEq)]
   |

```

The compiler not only tells us what the issue is but also suggests a solution for it. Let’s follow the instructions and annotate the `Book` struct with `#[derive(PartialEq)]`:

```
#[derive(PartialEq)]
struct Book {
    title: String,
    author: String
}

```

We can compare the books again and it should behave the same:

```
if my_book == your_book {
    println!("We have the same book!");
} else {
    println!("We have different books.");
}
// Prints: 
// We have different books.

```

`derive` generates the implementation of a trait for us. We don’t have to do the work ourselves. It provides a generally useful behavior that we don’t have to worry about. For example, in the case of derived `PartialEq` on structs, two instances are equal if all the fields are equal, and not equal if any fields aren’t equal.

We can derive multiple traits at the same time – `derive` accepts a list of all the required traits inside the parentheses. Let’s try to derive `Display` so we can print our books:

```
#[derive(PartialEq, Display)]
struct Book {
    title: String,
    author: String
}

```

Unfortunately, it doesn’t compile:

```
error: cannot find derive macro `Display` in this scope

```

It says that it can’t find the way to derive it in this scope. But the compiler won’t be able to find it in any scope! Because `Display` can’t be derived. The `Display` trait is used for user-facing output, and Rust cannot decide for you how to pretty print your type. Should it print the name of the struct? Should it print all the fields? You have to answer these and other questions yourself.

Deriving is limited to a certain number of traits. In case you want to implement a trait that can’t be derived or you want to implement a specific behavior for a derivable trait, you can write the functionality yourself.

* * *

💡 **Which standard library traits can be derived?**

Rust book’s [Appendix C: Derivable Traits](https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#appendix-c-derivable-traits) provides a reference of all the derivable traits in the standard library.

💡 **Can the non-standard-library traits be derived?**

Yes. Developers can implement `derive` for their own traits through [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros).

💡 **When should I implement and when should I derive traits?**

When deriving is available, it provides a general implementation of the trait, which is what you want most of the time. If it’s not the case, you can manually define the desired behavior.

* * *

## How to define a trait?

Until now, we have only been talking about other people’s traits. But we can also define our own traits.

Imagine that we want to estimate reading time. It shouldn’t matter what type of content we are reading (an article, a novel, or even a poem). We could use the following trait:

```
// [1]
trait Estimatable {
// [2] [3]
    fn estimate(&self) -> f64;
}

// [1]: The trait name (in this case, Estimatable).
// [2]: Trait methods (in this case, only one).
// [3]: The **&self** parameter, followed by other parameters (in this case, none)

```

**Note:** We are using a simple type for minutes for the sake of simplicity. Try not to do this at home! And try not to do this in production!

Any type implementing the `Estimatable` trait should define an `estimate` method that calculates how long it might take to read something in minutes.

Let’s try doing that.

### Implementing a trait for an enum

In the previous section, we implemented a trait for a struct. Let’s try working with an enum this time. Imagine that we have a personal blog with different kinds of content:

```
enum Content {
    Infographic,
    PersonalEssay { text: String },
    TechArticle { text: String, topic: String },
}

```

We can define the `Estimatable` trait for it. It’s not that different from implementing a trait for a struct, but note that we have to define behavior for all the enum variants:

```
impl Estimatable for Content {
    fn estimate(&self) -> f64 {
        match self {
            Content::Infographic => 1.0,

            Content::PersonalEssay { text } => 
                word_count(text) / AVG_WORDS_PER_MINUTE,

            // You have to pay attention, especially when learning Rust
            Content::TechArticle { text, topic } => {
                if topic == "Rust" {
                    word_count(text) / (0.5 * AVG_WORDS_PER_MINUTE)
                } else {
                    word_count(text) / (0.7 * AVG_WORDS_PER_MINUTE)
                }
            }
        }
    }
}

```

**Note:** For this example, imagine that `word_count` is a function that returns the number of words in a string and `AVG_WORDS_PER_MINUTE` is a constant for `250.0`.

### Default implementations

Let’s revisit the `PartialEq` trait. We saw a simplified version before, here is the actual definition:

```
pub trait PartialEq<Rhs = Self>
where
    Rhs: ?Sized,
{
    const fn eq(&self, other: &Rhs) -> bool;

    const fn ne(&self, other: &Rhs) -> bool { ... }
}

```

When we defined the `PartialEq` implementation for the `Book`, we only had to implement one method, `eq`. But the definition clearly has two methods. How can this be?

Trait definitions can provide default method definitions. `ne` is a method with a default implementation.

As an example, we can extend the `Estimatable` trait with a `display_estimate` method:

```
trait Estimatable {
    fn estimate(&self) -> f64;

    fn display_estimate(&self) -> String {
        format!("{} minute(s)", self.estimate())
    }
}

```

We don’t have to update the implementation for the `Content` type. It works as expected based on `estimate`:

```
println!("Estimated read: {}", Content::Infographic.display_estimate())
// Prints:
// Estimated read: 1 minute(s)

```

Sometimes it’s useful to have default behaviors for the trait methods. When someone implements the trait for their type, they can decide whether to keep or override the default implementations.

Remember how we talked about iterators? The standard library provides a lot of useful methods for [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) such as `map`, `filter`, and `fold`. As long as the collection implements either [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) or [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html), you get these for free.

```
(0..10) // A range of integers
  .filter(|x| x % 2 != 0) // Keep only odd numbers
  .map(|x| x * x) // Square each number
  .collect::<Vec<usize>>(); // Return a new Vector
// [1, 9, 25, 49, 81]

```

## How to use traits in function parameters?

Now that we know how to define our own traits, we can explore how to make use of them.

A trait is an interface that defines behaviors with a contract that other code can rely on. We can implement functions that depend on this interface.

In other words, we can implement functions that accept any type implementing our trait. The functions don’t need to know anything else about these types.

Let’s see how we could implement a function similar to `println!` that can print any `Estimatable` type:

```
// [1] [2] [3]  
fn summarize<T: Estimatable>(obj: T) {
    println!("Estimated read: {}", obj.display_estimate());
}

// [1]: The function takes any type T
// [2]: that is Estimatable.
// [3]: `obj` has a type T.

```

`<T: Estimatable>` declares a generic type parameter with a [trait bound](https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax). We can use trait bounds to restrict generics. In this case, it says that we accept any type `T` that implements the `Estimatable` trait. (If you would like to learn more about generic type parameters, see the [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html#generic-data-types) chapter of The Rust Book.)

In simple cases like this, we can use a more concise `impl Trait` syntax, which is syntactic sugar for trait bounds. We can rewrite it as follows:

```
// [1] [2] [3]
fn summarize(obj: impl Estimatable) {
    println!("Estimated read: {}", obj.display_estimate());
}

// [1]: The `obj` parameter should be a type
// [2]: that **impl** ements 
// [3]: the `Estimatable` trait.

```

The syntax `impl Trait` is convenient in simple cases, while the trait bound syntax can express more complex use cases.

Either way, we can now call `summarize`:

```
summarize(Content::Infographic)
// Prints:
// Estimated read: 1 minute(s)

```

If we try calling the `summarize` function with a type that doesn’t implement the `Estimatable` trait, the code won’t compile:

```
summarize(my_book)
// error: the trait bound `Book: Estimatable` is not satisfied

```

With traits, we can build functions that accept any type as long as it implements a certain behavior.

* * *

💡 **We can also use traits as return types from functions.**

For example, you can return an iterator from a function. Using traits in return types is less common than in parameters. If you are interested, make sure to explore [it in The Rust Book](https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits).

* * *

## How to extend traits?

Rust doesn’t have a concept of inheritance. However, you can define supertraits to specify that a trait is an extension of another trait. For example, we can add another trait that adds a paywall to long reads on the website. For this, we need the ability to estimate from `Estimatable`, so we’ll extend it.

```
// [1] [2][3]
trait PayWallable : Estimatable {
    fn use_pay_wall(&self) -> bool;
}

// To define 
// [1]: a subtrait,
// [2]: that extends 
// [3]: the supertrait.

```

If you want to implement a subtrait (`PayWallable`), you must implement all the required methods of the trait itself (`PayWallable`) as well as all the required methods of the supertrait (`Estimatable`). When you implement a method on a subtrait, you can use the functionality of the supertrait:

```
impl PayWallable for Book {
    fn use_pay_wall(&self) -> bool {
        self.estimate() > 10.0
    }
}

```

But if you don’t implement the supertrait, you will get a compiler error:

```
error[]: the trait bound `Book: Estimatable` is not satisfied
    |
    | impl PayWallable for Book {
    | ^^^^^^^^^^^ the trait `Estimatable` is not implemented for `Book`
    |

```

We can fix the error by implementing the supertrait as well:

```
impl Estimatable for Book {
    fn estimate(&self) -> f64 {
        let book_content = get_book_content_todo();
        word_count(&book_content) / AVG_WORDS_PER_MINUTE
    }
}

```

## Traits have many uses

In this blog post, we’ve covered one major use case for traits. Traits can be used as **interfaces**. Traits can serve as a contract that defines an interaction between components that use the interface. All types implementing a given trait must support the functionality defined by this trait, but it can be implemented differently for each type.

For example, a `sort` algorithm can be applied to a collection of items of any type, as long as they support comparison (implement the `Ord` trait).

But traits are more flexible than that. Look at what different things we can do with traits:

- Traits can be used to **extend the functionality** of types. We can use traits to add methods to types that are defined in other libraries. For example, the `itertools` library adds a lot of convenience methods to all iterators (outside of their definition).

- Traits can be used for **dynamic dispatch** (deciding which method to call at runtime). When used like this, they are very similar to Java interfaces.

There are many other uses: traits as abstract classes, as mix-ins, as [behavioral markers](https://doc.rust-lang.org/std/marker/index.html), and for [operator overloading](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#default-generic-type-parameters-and-operator-overloading).

## Conclusion

Understanding traits and how the standard library traits work is an important part of learning Rust.

We have learned that traits in Rust are a way to add functionality to structs or enums and define shared behavior between different types. We’ve also explored how to use traits as interfaces and peeked at the various uses of traits.

If you would like to learn more about traits and the problems they solve, check out the Rust book chapters [on traits](https://doc.rust-lang.org/book/ch10-02-traits.html) and [advanced traits](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#advanced-traits), and the Rust Blog [post about traits](https://blog.rust-lang.org/2015/05/11/traits.html).

And if you would like to read more beginner-friendly articles about Rust, be sure to follow us on [Twitter](https://twitter.com/serokell) or subscribe to our newsletter via the form below.