Unlock Your Python Backend Career: Build 30 Projects in 30 Days. Join now for just $54

Chapter 2: Advanced Rust Concepts

Traits

We’re done! We’ll compile and run our program with cargo run in the code directory.

Alas! We have an error:

error[E0599]: no method named `try_into` found for type `usize` in the current scope
  --> src/main.rs:10:25
   |
10 |     var.chars().count().try_into().expect("Length of name is too large!")
   |                         ^^^^^^^^ method not found in `usize`
   |
   = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
   |
1  | use std::convert::TryInto;
   |

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
error: could not compile `love_calculator`.

Something about traits.

A not very useful example

So, what are traits? Well, traits are a way Rust says: Inheritance.

As codebase increases, we notice that some structs seem to have similar characteristics, and it is important that future structs with such characteristics should be able to perform actions these structs can do.

An example, as culled from the Rust documentation will be used. Let’s say we’re implementing Tweets like so:

struct Tweet {
	username: String,
	content: String,
	location: String,
	retweet: bool,
}

We might want to implement a summarize method that displays a short form of the tweet:

impl Tweet {
   fn summarize(&self) → String {
	if self.retweet {
		format!(“{} retweeted: {} while in {}”, self.username, self.content, self.location)
	else {
		format!(“{} tweeted: {} while in {}”, self.username, self.content, self.location)
		}
	}
}

Consider another code where we implemented a news article like so:

struct Article {
	title: String,
	author: String,
	content: String,
}

and need to implement a summarize method. While we could as well implement it as a separate method:

impl Article {
	fn summarize(&self) → String {
		format!(“{} posted an article title: {}”, self.author, self.title)
	}
}

we could standardize our code by creating a trait both structs could implement. This way, we can ensure consistency when working with different structs with similar functionality.

So our trait is implemented like so:

trait Summary {
	fn summarize(&self) → String
}

Next, we attach the trait to our structs and implement the methods since it would be enforced anyway:

// for Tweet
impl Summary for Tweet {
	fn summarize(&self) → String {
		if self.retweet {
			format!(“{} retweeted: {} while in {}”, self.username, self.content, self.location)
		else {
			format!(“{} tweeted: {} while in {}”, self.username, self.content, self.location)
		}
	}
}

// for Article

impl Summary for Article {
	fn summarize(&self) → String {
		format!(“{} posted an article title: {}”, self.author, self.title)
	}
}

Then we can call our methods:

fn main() {
	let tweet = Tweet {
		String::from(“Lord_Sarcastic”),
		String::from(“Hello, world! Bye, world!”),
		String::from(“Lagos”),
		false
	}

	let article = Article {
		String::from(“Python and Rust?”),
		String::from(“Lord_Sarcastic”),
		String::from(“Python and Rust make a good match”)
	}

	println!(“Tweet summary: ‘{}’”, tweet.summarize());
	println!(“Article summary: ‘{}’”, article.summarize());
}

A very useful example

With traits, we could also have a default implementation for methods. As an example, if we wanted a display method that made use of the summarize method under the hood, we could have a trait like so:

trait Summary {
	fn summarize(&self) → String;

	fn display(&self) → String {
		format!(“Summary: {}”, self.summarize())
	}
}

At this point, both structs can call display without having implemented methods for it, but summarize must be implemented for it to work.

Lest we go offtrack…

Why this? The thing is for us to use functionalities that involve traits, the trait has to be in scope.

We can see that the Rust compiler insists that we bring a trait into scope before it would allow type conversion. That can be fixed with a simple line:

use std::convert:TryInto;

Whenever you're ready

There are 4 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Get Backend Jobs

Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board