Traits

Extension Traits

An extension trait is a local trait designed specifically to add new convenience methods to an existing external type—such as a primitive (str, [T]), a standard library struct (Result<T, E>, Option<T>), or a trait (Iterator)—without modifying the original crate.

Why Extension Traits Work

Rust forbids adding inherent methods (impl str { ... } or impl<T> Vec<T> { ... }) to types defined outside your crate. However, the orphan rule permits implementing a local trait for any external type. When callers import the extension trait (use crate::StrExt;), its methods become callable with standard dot syntax ("text".method()).

External Type (`str` in `core`)       Local Extension Trait (`SlugExt`)
+-----------------------------+       +----------------------------------+
| Inherent methods are fixed  | +---> | pub trait SlugExt {              |
| by standard library         |       |     fn to_slug(&self) -> String; |
+-----------------------------+       | }                                |
                                      | impl SlugExt for str { ... }     |
                                      +----------------------------------+

By convention, extension traits are named with an Ext suffix (such as SliceExt, IteratorExt, ReadExt, or FutureExt).

Extending str and Slices [T]

Implementing an extension trait directly on the unsized slice types str and [T] makes the methods callable on &str, String, &[T], Vec<T>, and [T; N] simultaneously via auto-deref:

pub trait StrExt {
    fn to_kebab_case(&self) -> String;
}

impl StrExt for str {
    fn to_kebab_case(&self) -> String {
        self.split_whitespace()
            .map(str::to_ascii_lowercase)
            .collect::<Vec<_>>()
            .join("-")
    }
}

fn main() {
    let literal: &str = "Rust Extension Traits";
    let owned: String = String::from("Zero Cost Abstraction");

    println!("{}", literal.to_kebab_case());
    println!("{}", owned.to_kebab_case());
}
rust-extension-traits
zero-cost-abstraction

Blanket Extension Traits Over Another Trait (IteratorExt)

Combining an extension trait with a supertrait bound and a blanket implementation attaches new adapter or terminal methods to every type implementing a base trait (such as Iterator):

pub trait IteratorExt: Iterator {
    /// Counts how many items satisfy a predicate while consuming the iterator.
    fn count_where<P>(self, mut predicate: P) -> usize
    where
        Self: Sized,
        P: FnMut(&Self::Item) -> bool,
    {
        let mut count = 0;
        for item in self {
            if predicate(&item) {
                count += 1;
            }
        }
        count
    }
}

// Blanket implementation: EVERY `I: Iterator` automatically implements `IteratorExt`
impl<I: Iterator + ?Sized> IteratorExt for I {}

fn main() {
    let numbers = [3, -1, 8, 0, -5, 12];
    let positive_count = numbers.iter().count_where(|&&n| n > 0);

    println!("Positive numbers: {positive_count}");
}
Positive numbers: 3

Notice two idiomatic details in IteratorExt:

  1. Every method bodies lives directly inside trait IteratorExt as a default implementation, so impl<I: Iterator + ?Sized> IteratorExt for I {} has an empty body {}.
  2. Sealing IteratorExt (preventing downstream crates from writing custom impl IteratorExt overrides) ensures no external type can override the extension method with inconsistent behavior.