Rust LogoGeneric Data Types

Generic Data Types are a fundamental concept in modern programming languages, including Rust, that enable you to write flexible and reusable code. Instead of writing separate code for each specific data type (e.g., `i32`, `f64`, `String`), generics allow you to define functions, structs, enums, and traits that work with *any* data type, or a constrained set of data types.

Why use Generics?
1. Code Reusability: You can write a single implementation that works for multiple types, reducing code duplication. For example, a `List` data structure can hold integers, strings, or custom objects without needing three separate `IntList`, `StringList`, and `ObjectList` implementations.
2. Type Safety: Generics provide compile-time type checking. This means that although your code is generic, the Rust compiler still ensures that the types being used are compatible and prevents common type-related errors that might only appear at runtime in languages without strong type checking.
3. Performance: In Rust, generics are typically implemented via a process called *monomorphization*. This means that at compile time, the compiler generates a specific version of the generic code for each concrete type that the generic code is used with. For example, if you use a `List<i32>` and a `List<String>`, the compiler will generate two distinct `List` implementations: one for `i32` and one for `String`. This ensures that there is no runtime overhead associated with generics, as all type information is resolved at compile time.

How Generics work in Rust:
Generics are declared using angle brackets (`<T>`) where `T` (or any uppercase letter) represents a placeholder for a type.

* Generic Functions: You can make functions generic over some types.
```rust
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { /* ... */ }
```
Here, `T` is a generic type parameter. `PartialOrd + Copy` are *trait bounds*, meaning `T` must implement these traits.

* Generic Structs: Structs can hold fields of generic types.
```rust
struct Point<T> {
x: T,
y: T,
}
```
This `Point` struct can store coordinates of any type `T`.

* Generic Enums: Enums can also be generic.
```rust
enum Option<T> {
Some(T),
None,
}
```
The standard library's `Option<T>` and `Result<T, E>` are prime examples of generic enums.

Benefits of Rust's Generics:
* Zero-cost abstractions: As mentioned, monomorphization ensures that generic code runs just as fast as if you had written out duplicate code for each type manually.
* Expressiveness: They allow expressing powerful ideas like "this function works for *any* type that can be ordered" without sacrificing safety or performance.
* Strong Type System: Rust's type system, combined with generics, catches a vast majority of programming errors at compile time, leading to more robust software.

In summary, generic data types are a powerful feature that allows you to write flexible, reusable, and type-safe code without compromising on performance. They are an essential tool in Rust for building robust and efficient applications.

Example Code

```rust
// 1. Generic Struct
// A struct that can hold two values of the same generic type T.
struct Pair<T> {
    first: T,
    second: T,
}

impl<T> Pair<T> {
    // A generic method to create a new Pair.
    fn new(first: T, second: T) -> Pair<T> {
        Pair { first, second }
    }

    // A generic method to swap the values.
    fn swap(&mut self) {
        std::mem::swap(&mut self.first, &mut self.second);
    }
}

// 2. Generic Function
// A function that finds the largest item in a list.
// The type T must implement the PartialOrd trait for comparison
// and the Copy trait so we can return a copy of the item.
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest_item = list[0];

    for &item in list.iter() {
        if item > largest_item {
            largest_item = item;
        }
    }
    largest_item
}

// 3. Generic Enum (demonstrating standard library's Option)
// This is how Option is defined in Rust's standard library.
// We're just showing it here as an example of a generic enum.
// enum Option<T> {
//     Some(T),
//     None,
// }

fn main() {
    // --- Using Generic Struct ---
    // Create a Pair of integers
    let mut int_pair = Pair::new(10, 20);
    println!("Initial int_pair: ({}, {})", int_pair.first, int_pair.second);
    int_pair.swap();
    println!("Swapped int_pair: ({}, {})", int_pair.first, int_pair.second);

    // Create a Pair of floating-point numbers
    let mut float_pair = Pair::new(3.14, 2.71);
    println!("Initial float_pair: ({}, {})", float_pair.first, float_pair.second);
    float_pair.swap();
    println!("Swapped float_pair: ({}, {})", float_pair.first, float_pair.second);

    // Create a Pair of characters
    let mut char_pair = Pair::new('a', 'b');
    println!("Initial char_pair: ({}, {})", char_pair.first, char_pair.second);
    char_pair.swap();
    println!("Swapped char_pair: ({}, {})", char_pair.first, char_pair.second);

    // --- Using Generic Function ---
    let number_list = vec![34, 50, 25, 100, 65];
    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];
    let result = largest(&char_list);
    println!("The largest char is {}", result);

    // Note: For types like `String` or `&str`, the `largest` function's type bounds (`Copy`) 
    // would need to be adjusted (e.g., using `Clone` and returning an owned value, or 
    // working with references `&T` instead of owned `T`). For simplicity, this example 
    // sticks to primitive types that implement `Copy`.

    // --- Using Generic Enum (Option) ---
    let some_number: Option<i32> = Some(5);
    let no_number: Option<i32> = None;

    match some_number {
        Some(x) => println!("Got a number: {}", x),
        None => println!("No number here!"),
    }

    let some_string: Option<String> = Some(String::from("Hello Generics!"));
    if let Some(s) = some_string {
        println!("{}", s);
    }
}
```