Generics in Rust

Generics in Rust

Introduction

Generics are how we handle multiple data types effectively.

It exists in Rust, as well as in many other Programming languages.

Example

fn compare_values<T>(value1: T, value2: T) -> bool
where
    T: PartialEq,
{
    value1 == value2
}

fn main() {
    let result_int = compare_values(42, 42);
    println!("{}", result_int); // true

    let result_float = compare_values(3.14, 2.71);
    println!("{}", result_float); // false

    let result_str = compare_values("hello", "world");
    println!("{}", result_str); // false
}

We have the compare_values function that takes in two values of type T (which could be any type)

Now T should have the PartialEq trait, which means that == equality operator must be valid for T (we will dicuss about Traits in detail separately)

Lastly the function will return a bool, after checking for equality of the two values. And the results are printed accordingly.

T is called the generic type. In absence of it we'd have to write 3 functions for comparing int, float & str

Something like this.

fn compare_values_int(value1: i32, value2: i32) -> bool {
    value1 == value2
}

fn compare_values_float(value1: f64, value2: f64) -> bool {
    value1 == value2
}

fn compare_values_str(value1: &str, value2: &str) -> bool {
    value1 == value2
}

fn main() {
    let result_int = compare_values_int(42, 42);
    println!("{}", result_int); // true

    let result_float = compare_values_float(3.14, 2.71);
    println!("{}", result_float); // false

    let result_str = compare_values_str("hello", "world");
    println!("{}", result_str); // false
}

And avoiding this is how generics makes our program more effective.

Now in this Program we have only one generic type T

But functions can take multiple generic types also.

fn are_types_equal<T: 'static, U: 'static>() -> bool {
    std::any::TypeId::of::<T>() == std::any::TypeId::of::<U>()
}

fn main() {
    let result_same = are_types_equal::<i32, i32>();
    println!("{}", result_same); // true

    let result_diff = are_types_equal::<i32, f64>();
    println!("{}", result_diff); // false
}

In the above example the are_types_equal accepts two generic types T & U, compares them and returns the result.

Now apart from functions generics can also be implemented in structs & enums.

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

Option is widely used in Rust to declare fields as optional.

enum Option<T> {
    Some(T),
    None,
}

Some is the presence of value of type T. Else we have None.

(we will discuss more about this in the next article)

Conclusion

That's all we need to know about Generics. Learning about it will help us a lot in understanding more advanced Rusty concepts. Cheers!