Generics

Generic Methods

A generic method is a method attached to a struct, enum, or trait that operates on generic type parameters—either the type parameters declared on the enclosing impl<T> block, additional type parameters declared on the fn signature itself, or both.

Methods Using the Type's Generic Parameter

To define methods on a generic type Point<T> that work for any T, declare T right after impl (impl<T> Point<T>). That declaration tells the compiler that the T in Point<T> is a generic type parameter rather than a concrete type named T:

struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }

    fn into_tuple(self) -> (T, T) {
        (self.x, self.y)
    }
}

fn main() {
    let p = Point { x: 12, y: 34 };
    println!("p.x() = {}", p.x());

    let (x, y) = p.into_tuple();
    println!("tuple = ({x}, {y})");
}
p.x() = 12
tuple = (12, 34)

Methods Restricted to a Specific Concrete Type

An impl block can omit <T> after impl and name a specific concrete instantiation such as impl Point<f64>. Methods inside that block exist only on Point<f64> and are unavailable on Point<i32> or any other instantiation:

struct Point<T> {
    x: T,
    y: T,
}

impl Point<f64> {
    fn distance_from_origin(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

fn main() {
    let p = Point { x: 3.0_f64, y: 4.0_f64 };
    println!("Distance: {}", p.distance_from_origin());
}
Distance: 5

Calling distance_from_origin() on a Point<i32> produces a compile-time error (E0599) because no such method exists for Point<{integer}>:

// ❌ does not compile
struct Point<T> {
    x: T,
    y: T,
}

impl Point<f64> {
    fn distance_from_origin(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

fn main() {
    let int_point = Point { x: 3, y: 4 };
    println!("{}", int_point.distance_from_origin());
}
error[E0599]: no method named `distance_from_origin` found for struct `Point<{integer}>` in the current scope
  --> src/main.rs:15:30
   |
2  | struct Point<T> {
   | --------------- method `distance_from_origin` not found for this struct
...
15 |     println!("{}", int_point.distance_from_origin());
   |                              ^^^^^^^^^^^^^^^^^^^^ method not found in `Point<{integer}>`
   |
   = note: the method was found for
           - `Point<f64>`

Methods Introducing Their Own Type Parameters

A method inside impl<T> Point<T> can declare additional type parameters on its own fn signature (such as fn mixup<U>(...)) that are independent of T:

#[derive(Debug)]
struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c' };

    let p3 = p1.mixup(p2);
    println!("p3 = ({}, {})", p3.x, p3.y);
}
p3 = (5, c)

Here X1 and Y1 belong to the receiver's impl<X1, Y1> block, while X2 and Y2 are scoped strictly to the mixup method and inferred from the other argument.

Type Parameter Placement Scope When Bound
impl<T> Type<T> All methods inside the impl block When the Type<T> instance is constructed
fn method<U>(&self, arg: U) Only that single method Each time method is called on an instance