← Back to Rust Course | Chapter 11: Generics & Traits | Lesson 3 of 7

Trait Basics

A trait is a promise that says "any type using me knows how to do these particular things."
Syntax
rust
trait Name {
    fn method(&self) -> ReturnType;
}

impl Name for Type {
    fn method(&self) -> ReturnType {
        // body
    }
}

Defining a Trait

The trait keyword defines a set of method signatures that any implementing type must provide, describing shared behavior without dictating how it's stored.

Example: Defining a Trait

markup
// Define a trait `Greet` describing shared behavior
trait Greet {
    // Define the function `greet` taking `&self` and returning a `String;`
    fn greet(&self) -> String;
}

// Define a struct `Person` to group related fields together
struct Person {
    name: String,
}

// Implement the `Greet` trait for `Person`
impl Greet for Person {
    // Define the function `greet` taking `&self` and returning a String
    fn greet(&self) -> String {
        format!("Hello, my name is {}", self.name)
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `p`, set to `Person { name: String::from("Ada") }`
    let p = Person { name: String::from("Ada") };
    // Print "{}" to the console, substituting in `p.greet()` (with a trailing newline)
    println!("{}", p.greet());
}

Implementing a Trait for Multiple Types

The same trait can be implemented for several unrelated types, each providing its own version of the required behavior.

Example: Implementing a Trait for Multiple Types

markup
// Define a trait `Greet` describing shared behavior
trait Greet {
    // Define the function `greet` taking `&self` and returning a `String;`
    fn greet(&self) -> String;
}

// Define a struct `Robot` to group related fields together
struct Robot;

// Implement the `Greet` trait for `Robot`
impl Greet for Robot {
    // Define the function `greet` taking `&self` and returning a String
    fn greet(&self) -> String {
        String::from("BEEP BOOP HELLO")
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `r`, set to `Robot`
    let r = Robot;
    // Print "{}" to the console, substituting in `r.greet()` (with a trailing newline)
    println!("{}", r.greet());
}

A Type Implementing Multiple Traits

A single type can implement as many different traits as needed, each contributing a different piece of behavior to that type.

Example: A Type Implementing Multiple Traits

markup
// Define a trait `Greet` describing shared behavior
trait Greet {
    // Define the function `greet` taking `&self` and returning a `String;`
    fn greet(&self) -> String;
}

// Define a trait `Farewell` describing shared behavior
trait Farewell {
    // Define the function `farewell` taking `&self` and returning a `String;`
    fn farewell(&self) -> String;
}

// Define a struct `Person` to group related fields together
struct Person {
    name: String,
}

// Implement the `Greet` trait for `Person`
impl Greet for Person {
    // Define the function `greet` taking `&self` and returning a String
    fn greet(&self) -> String {
        format!("Hi, {}", self.name)
    }
}

// Implement the `Farewell` trait for `Person`
impl Farewell for Person {
    // Define the function `farewell` taking `&self` and returning a String
    fn farewell(&self) -> String {
        format!("Bye, {}", self.name)
    }
}

// Entry point: execution of the program starts here
fn main() {
    let p = Person { name: String::from("Lee") };
    println!("{}", p.greet());
    println!("{}", p.farewell());
}

Calling Trait Methods Through the Trait

Once a trait is implemented for a type, its methods are called using ordinary dot syntax, exactly like any other method, as long as the trait is in scope.

Example: Calling Trait Methods Through the Trait

markup
// Define a trait `Area` describing shared behavior
trait Area {
    // Define the function `area` taking `&self` and returning a `f64;`
    fn area(&self) -> f64;
}

// Define a struct `Square` to group related fields together
struct Square {
    side: f64,
}

// Implement the `Area` trait for `Square`
impl Area for Square {
    // Define the function `area` taking `&self` and returning a floating-point number
    fn area(&self) -> f64 {
        self.side * self.side
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `sq`, set to `Square { side: 4.0 }`
    let sq = Square { side: 4.0 };
    // Print "Area: {}" to the console, substituting in `sq.area()` (with a trailing newline)
    println!("Area: {}", sq.area());
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting to bring a trait into scope with use before calling its methods on a type, even if the impl exists elsewhere.
  2. Confusing a trait definition with a struct -- traits describe shared behavior, they do not themselves hold data.
  3. Implementing a trait's method with a different signature than the trait declares, which fails to compile.
Chapter Summary
  • A trait defines a set of method signatures that implementing types agree to provide.
  • impl TraitName for TypeName { ... } provides the actual behavior for a specific type.
  • A single type can implement multiple different traits.
  • Traits enable shared behavior across otherwise unrelated types, similar to interfaces in other languages.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.