Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

Rust Dictionary

  1. Home
  2. Rust Dictionary
  3. trait / impl Trait for Type

trait / impl Trait for Type

A trait in Rust defines a set of methods that a type must implement — similar to an interface. You define a trait with the trait keyword and implement it with impl Trait for Type. Traits can also provide default method implementations.

Syntax

// Define a trait.
trait Greet {
    // Required method: must be implemented by the type.
    fn name(&self) -> &str;

    // Default implementation: used as-is, but can be overridden.
    fn greet(&self) {
        println!("Hello, {}!", self.name());
    }
}

struct English {
    name: String,
}

// Implement the trait for the type.
impl Greet for English {
    fn name(&self) -> &str {
        &self.name
    }
    // greet() uses the default implementation.
}

// Accept a trait as an argument (impl Trait syntax).
fn introduce(item: &impl Greet) {
    item.greet();
}

Syntax and Concepts

Syntax / ConceptDescription
trait Foo { fn method(&self); }Defines a trait.
fn method(&self) { ... }Provides a default implementation inside a trait.
impl Foo for Type { ... }Implements a trait for a type.
fn func(x: &impl Foo)impl Trait syntax: accepts any type that implements the trait.
fn func<T: Foo>(x: &T)Trait bound syntax: same as above, using generics.
fn func() -> impl FooReturns a type that implements the trait.
dyn FooDynamic dispatch: uses the trait as a trait object.
Box<dyn Foo>A heap-allocated trait object (type erasure).

Sample Code

trait Area {
    fn area(&self) -> f64;

    // Default implementation: formats and prints the area.
    fn describe(&self) {
        println!("Area: {:.2}", self.area());
    }
}

trait Perimeter {
    fn perimeter(&self) -> f64;
}

struct Circle {
    radius: f64,
}

struct Rectangle {
    width: f64,
    height: f64,
}

impl Area for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

impl Area for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
    // describe() uses the default implementation.
}

impl Perimeter for Rectangle {
    fn perimeter(&self) -> f64 {
        2.0 * (self.width + self.height)
    }
}

// impl Trait syntax: accepts any type that implements Area.
fn print_area(shape: &impl Area) {
    shape.describe();
}

// Dynamic dispatch: handles trait objects of different types together.
fn largest_area(shapes: &[Box<dyn Area>]) -> f64 {
    shapes.iter().map(|s| s.area()).fold(0.0_f64, f64::max)
}

fn main() {
    let c = Circle { radius: 3.0 };
    let r = Rectangle { width: 4.0, height: 6.0 };

    // Call polymorphically via impl Trait.
    print_area(&c);
    print_area(&r);

    // Mix different types in a Vec using Box<dyn Trait>.
    let shapes: Vec<Box<dyn Area>> = vec![
        Box::new(Circle { radius: 1.0 }),
        Box::new(Rectangle { width: 3.0, height: 4.0 }),
        Box::new(Circle { radius: 5.0 }),
    ];

    for shape in &shapes {
        shape.describe();
    }
    println!("Largest area: {:.2}", largest_area(&shapes));
}

Notes

Rust's orphan rule restricts trait implementations: you can only implement a trait for a type if either the trait or the type is defined in your own crate. You cannot implement an external crate's trait for an external crate's type.

impl Trait uses static dispatch (monomorphization), where the concrete type is resolved at compile time. dyn Trait uses dynamic dispatch, where the type is resolved at runtime. Static dispatch is faster, but dynamic dispatch lets you collect values of different types into a single collection.

For the Display and Debug traits, see Display Trait / Debug Trait.

If you find any errors or copyright issues, please .