Skip to content
DocsRust LearningadvancedGenerics & Traits
Chapter 10 of 19·advanced·10 min read

Generics & Traits

Generic & Trait

Polymorphism and abstraction in Rust

Hover or tap any paragraph to see Vietnamese translation

Generic Functions

Generics let you write code that works over multiple types without duplication. Instead of writing separate functions for i32 and f64, you write one function with a type parameter T.

generic_fn.rs
1// Without generics — duplicated for each type2fn largest_i32(list: &[i32]) -> &i32 {3    let mut largest = &list[0];4    for item in list {5        if item > largest { largest = item; }6    }7    largest8}910// With generics — one function for any PartialOrd type11fn largest<T: PartialOrd>(list: &[T]) -> &T {12    let mut largest = &list[0];

The type parameter T is declared in angle brackets <T> after the function name. The PartialOrd trait bound tells the compiler that T must support comparison.

Generic Structs and Enums

Structs and enums can also be parameterized by type. You have already used generic types like Option<T> and Result<T, E> — they are defined this way.

generic_structs.rs
1// Generic struct with one type parameter2#[derive(Debug)]3struct Point<T> {4    x: T,5    y: T,6}78// Generic struct with two type parameters9#[derive(Debug)]10struct Pair<T, U> {11    first: T,12    second: U,

Generic Method Implementations

When implementing methods on a generic struct you must re-declare the type parameter after the impl keyword. You can also implement methods only for specific types using bounds.

generic_impl.rs
1#[derive(Debug)]2struct Point<T> {3    x: T,4    y: T,5}67// Methods available for all T8impl<T> Point<T> {9    fn new(x: T, y: T) -> Self {10        Point { x, y }11    }12

Trait Definitions

A trait defines functionality that a type can share with other types. They are similar to interfaces in other languages but with some important differences.

traits.rs
1// Define a trait2trait Summary {3    // Required method — implementors must provide this4    fn summarize(&self) -> String;56    // Required method with signature only7    fn author(&self) -> String;8}910trait Greet {11    fn name(&self) -> &str;12

Implementing Traits

You can implement a trait for any type you define, and you can also implement standard library traits for your types. The orphan rule: at least one of the trait or the type must belong to your crate.

impl_traits.rs
1use std::fmt;23struct Matrix([[f64; 2]; 2]);45// Implement Display for our custom type6impl fmt::Display for Matrix {7    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {8        write!(9            f,10            "[{:.2} {:.2}]11[{:.2} {:.2}]",12            self.0[0][0], self.0[0][1],

Default Implementations

Traits can provide default implementations for some or all methods. Types implementing the trait can keep or override the defaults.

default_impl.rs
1trait Describable {2    fn name(&self) -> &str;34    // Default implementation that calls required method5    fn describe(&self) -> String {6        format!("I am a {}", self.name())7    }89    // Default with more logic10    fn verbose_describe(&self) -> String {11        let base = self.describe();12        format!("{base} [described at runtime]")

Trait Bounds

Trait bounds specify that a generic type parameter must implement certain traits. This ensures you can call that trait's methods on the value.

trait_bounds.rs
1use std::fmt::{Display, Debug};23// Syntax 1: inline bound with colon4fn print_item<T: Display>(item: T) {5    println!("{item}");6}78// Multiple bounds with +9fn print_debug_and_display<T: Display + Debug>(item: T) {10    println!("Display: {item}");11    println!("Debug: {item:?}");12}

where Clauses

When you have many complex trait bounds, where clauses make function signatures more readable by moving the bounds to a separate location.

where_clauses.rs
1use std::fmt::{Debug, Display};2use std::ops::Add;34// Hard to read with inline bounds5fn complex_inline<T: Display + Debug + Clone, U: Display + Add<Output = U>>(6    t: T,7    u: U,8) -> String {9    format!("{t:?}")10}1112// Easier to read with where clause

Returning Types That Implement Traits (impl Trait)

The impl Trait syntax in return position lets you return some type that implements a trait without naming the concrete type. The compiler infers the actual type.

impl_trait_return.rs
1// Return "some type that implements Iterator<Item = i32>"2fn make_counter(start: i32, end: i32) -> impl Iterator<Item = i32> {3    start..=end4}56// Return "some type that implements Fn(i32) -> i32"7fn make_adder(x: i32) -> impl Fn(i32) -> i32 {8    move |n| n + x9}1011trait Animal {12    fn sound(&self) -> &str;

Trait Objects (dyn Trait)

When you need to store or return values of different concrete types that all implement the same trait, use trait objects (dyn Trait). This uses dynamic dispatch through a vtable, unlike the static dispatch of generics.

trait_objects.rs
1trait Draw {2    fn draw(&self);3}45struct Circle { radius: f64 }6struct Rectangle { width: f64, height: f64 }78impl Draw for Circle {9    fn draw(&self) { println!("Circle r={}", self.radius); }10}11impl Draw for Rectangle {12    fn draw(&self) { println!("Rect {}x{}", self.width, self.height); }
Tip
Use generics (impl Trait) when types are known at compile time — better performance. Use dyn Trait when you need different types in the same collection — more flexible.

Common Standard Library Traits

Essential Traits

  • Display — user-friendly formatting, used by println!("{}")
  • Debug — developer formatting, used by println!("{:?}"), derivable
  • Clone — explicit value duplication, commonly derived
  • Copy — implicit duplication for small stack types (i32, bool, etc.)
  • PartialEq, Eq — equality comparison (== and !=)
  • PartialOrd, Ord — ordering comparison (<, >, <=, >=)
  • Hash — required to use a type as a HashMap key
  • Default — provides a default value (commonly derived)
  • Iterator — the core iteration protocol, requires next()
  • From, Into — infallible type conversion
  • TryFrom, TryInto — fallible type conversion, returns Result
derive_traits.rs
1// Many traits can be derived automatically2#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]3struct Color {4    r: u8,5    g: u8,6    b: u8,7}89use std::fmt;10impl fmt::Display for Color {11    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {12        write!(f, "#{:02X}{:02X}{:02X}", self.r, self.g, self.b)

Key Takeaways

Điểm Chính

  • Generics let you write code that works with multiple typesGeneric cho phép viết code hoạt động với nhiều kiểu dữ liệu
  • Trait bounds constrain generics to types with specific capabilitiesRàng buộc trait giới hạn generic cho các kiểu có khả năng cụ thể
  • impl Trait uses static dispatch; dyn Trait uses dynamic dispatchimpl Trait dùng dispatch tĩnh; dyn Trait dùng dispatch động
  • Default trait methods provide fallback implementationsPhương thức mặc định của trait cung cấp triển khai dự phòng

Practice

Test your understanding of this chapter

Quiz

What does the PartialOrd trait bound in fn largest<T: PartialOrd> guarantee about T?

Ràng buộc trait PartialOrd trong fn largest<T: PartialOrd> đảm bảo điều gì về T?

Quiz

What is the key behavioral difference between impl Trait and Box<dyn Trait> in return position?

Sự khác biệt hành vi chính giữa impl Trait và Box<dyn Trait> ở vị trí trả về là gì?

True or False

A trait's default method implementation can be overridden by any type that implements the trait.

Triển khai phương thức mặc định của trait có thể được ghi đè bởi bất kỳ kiểu nào triển khai trait đó.

True or False

The orphan rule allows you to implement any external trait for any external type, as long as you do it inside your own crate.

Quy tắc mồ côi cho phép bạn triển khai bất kỳ trait bên ngoài nào cho bất kỳ kiểu bên ngoài nào, miễn là bạn làm điều đó trong crate của mình.

Code Challenge

Add a where clause with multiple bounds

Thêm mệnh đề where với nhiều ràng buộc

use std::fmt::{Debug, Display};

fn print_item<T>(item: T)
where
    T: Display + ,
{
    println!("{item} / {item:?}");
}

Chapter Complete!

Great job! Keep the momentum going.

Your progress0 of 19 chapters read
← → to navigate chapters
Built: 4/8/2026, 12:01:11 PM