Generic impl
Goal of This Episode
Learn to implement methods for a generic struct, and understand what the two Ts in the impl<T> syntax mean.
Concept
In Episode 2 we defined the generic struct Pair<T>. This episode we give it an impl.
Recall from Chapter 3, impl on a struct looks like this:
struct Point {
x: i32,
y: i32,
}
impl Point {
fn sum(&self) -> i32 {
self.x + self.y
}
}
fn main() {}
What about a generic struct?
The impl<T> Syntax
#[derive(Debug)]
struct Pair<T> {
first: T,
second: T,
}
impl<T> Pair<T> {
fn new(first: T, second: T) -> Pair<T> {
Pair { first, second }
}
}
fn main() {}
Note there are two Ts in different positions, playing different roles:
- The
<T>inimpl<T>: declares a type parameterT. It tells Rust “I’m about to use a type parameter namedT.” - The
<T>inPair<T>: uses the just-declaredT. It tells Rust “the type I’m implementing for isPair<T>.”
In other words: impl<T> declares T, then hands T to Pair<T> — “for any type T, implement the following methods for Pair<T>.”
If you wrote only impl Pair<T> without the impl<T>, Rust would assume T is a concrete type name (like i32 or String), fail to find a type called T, and report an error.
Conversely, writing impl Pair<i32> (no impl<T> needed) adds methods only to Pair<i32> — Pair<String> and the rest get nothing.
Using T inside Methods
Once T is declared, the whole impl block can use it:
#[derive(Debug)]
struct Pair<T> {
first: T,
second: T,
}
impl<T> Pair<T> {
fn new(first: T, second: T) -> Pair<T> {
Pair { first, second }
}
fn first(&self) -> &T {
&self.first
}
}
fn main() {}
Implementing traits Works the Same Way
When Chapter 4 taught traits, we implemented them for concrete types, like impl Greet for Cat. To implement a trait for a generic type, the syntax is the same — declare the type parameter after impl:
trait SomeTrait {}
struct Pair<T> {
first: T,
second: T,
}
impl<T> SomeTrait for Pair<T> {
// ...
}
fn main() {}
Again: “for any type T, implement this trait for Pair<T>.”
Example Code
#[derive(Debug)]
struct Pair<T> {
first: T,
second: T,
}
impl<T> Pair<T> {
// Associated function
fn new(first: T, second: T) -> Pair<T> {
Pair { first, second }
}
// Method: return a reference to first
fn first(&self) -> &T {
&self.first
}
// Method: return a reference to second
fn second(&self) -> &T {
&self.second
}
}
fn main() {
let p = Pair::new(10, 20);
println!("first = {}", p.first());
println!("second = {}", p.second());
println!("{:?}", p);
let q = Pair::new("hello", "world");
println!("first = {}", q.first());
println!("second = {}", q.second());
}
Recap
- To implement methods for a generic
struct, writeimpl<T> Pair<T> { ... }. - The
<T>inimpl<T>declaresT; the<T>inPair<T>usesT. - Bottom line:
impl<T>declaresT, then hands it toPair<T>. - Once declared, every method in the
implblock can useT. - Implementing a
traitfor a generic type is similar:impl<T> SomeTrait for Pair<T> { ... }.