9.9 Generic Structs

Structs can be generic, allowing them to work with different concrete types.

// Generic struct `Point<T>`
struct Point<T> {
    x: T,
    y: T,
}

// Generic struct with multiple type parameters
struct Pair<T, U> {
    first: T,
    second: U,
}

fn main() {
    // Instantiate with inferred types
    let integer_point = Point { x: 5, y: 10 }; // Point<i32>
    let float_point = Point { x: 1.0, y: 4.0 }; // Point<f64>
    let pair = Pair { first: "hello", second: 123 }; // Pair<&str, i32>

    println!("Int Point: x={}, y={}", integer_point.x, integer_point.y);
    println!("Float Point: x={}, y={}", float_point.x, float_point.y);
    println!("Pair: first={}, second={}", pair.first, pair.second);
}

9.9.1 Methods on Generic Structs

Methods can be defined on generic structs using impl<T>.

struct Point<T> { x: T, y: T }
impl<T> Point<T> {
    // This method works for any T
    fn x(&self) -> &T {
        &self.x
    }
}

fn main() {
    let p = Point { x: 5, y: 10 };
    println!("x coordinate: {}", p.x());
}

9.9.2 Constraining Generic Types with Trait Bounds

Trait bounds restrict generic types to those implementing specific traits, enabling methods that require certain capabilities.

use std::fmt::Display; // For printing
use std::ops::Add;    // For addition

struct Container<T> {
    value: T,
}

impl<T> Container<T> {
    fn new(value: T) -> Self { Container { value } }
}

// Method only available if T implements Display
impl<T: Display> Container<T> {
    fn print(&self) {
        println!("Container holds: {}", self.value);
    }
}

// Method only available if T implements Add<Output=T> + Copy
// (Requires T can be added to itself and is copyable)
impl<T: Add<Output = T> + Copy> Container<T> {
    fn add_to_self(&self) -> T {
        self.value + self.value // Requires Add and Copy
    }
}

fn main() {
    let c_int = Container::new(10);
    c_int.print(); // Works (i32 implements Display)
    println!("Doubled: {}", c_int.add_to_self()); // Works (i32 implements Add + Copy)

    let c_str = Container::new("hello");
    c_str.print(); // Works (&str implements Display)
    // println!("Doubled: {}", c_str.add_to_self()); Error! &str doesn't impl Add, Copy
}

Trait bounds are central to Rust’s polymorphism and type safety with generics.