Rust Interview Questions

35 Questions
Rust

Rust

Web DevelopmentIoT

Question 21

Explain the concept of generics in Rust.

Answer:

Generics in Rust allow you to write flexible and reusable code by enabling the definition of functions, structs, enums, and traits that can operate on different types without sacrificing type safety. Generics are a powerful feature that lets you abstract over types, making your code more versatile and concise.

Defining Generics

Generics are defined using angle brackets (<T>) where T is a placeholder for a type. You can use any identifier in place of T, but T is commonly used as a convention for "type."

Example with Functions

Here's a simple example of a generic function:

fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    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);
}

In this example, largest is a function that works with any type T that implements the PartialOrd trait, which is necessary for comparison operations.

Generic Structs

You can define structs with generic type parameters:

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

fn main() {
    let integer_point = Point { x: 5, y: 10 };
    let float_point = Point { x: 1.0, y: 4.0 };

    println!("integer_point: ({}, {})", integer_point.x, integer_point.y);
    println!("float_point: ({}, {})", float_point.x, float_point.y);
}

Generic Enums

Enums can also be generic:

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

enum Result<T, E> {
    Ok(T),
    Err(E),
}

These are standard library enums that are generic over the type T (for Option) and types T and E (for Result).

Generic Traits

Traits can be defined to be generic and implemented for multiple types:

trait Summarizable {
    fn summary(&self) -> String;
}

struct NewsArticle {
    headline: String,
    content: String,
}

impl Summarizable for NewsArticle {
    fn summary(&self) -> String {
        format!("{} - {}", self.headline, self.content)
    }
}

fn main() {
    let article = NewsArticle {
        headline: String::from("Breaking News"),
        content: String::from("Something happened!"),
    };

    println!("Article summary: {}", article.summary());
}

Constraints on Generics

You can constrain generic types to ensure they implement certain traits. This is done using trait bounds.

Example with Trait Bounds

fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

Here, T: PartialOrd is a trait bound that ensures T implements the PartialOrd trait, which allows comparison using the > operator.

Lifetime Parameters with Generics

Sometimes, you need to combine generics with lifetime parameters. Lifetimes ensure that references are valid as long as needed to avoid dangling references.

Example with Generics and Lifetimes

fn longest<'a, T>(x: &'a T, y: &'a T) -> &'a T
where
    T: PartialOrd,
{
    if x > y {
        x
    } else {
        y
    }
}

In this example, the function longest takes two references with the same lifetime 'a and returns a reference with the same lifetime. The generic type T must also implement PartialOrd.

Summary

  • Generics: Allow for writing flexible and reusable code by abstracting over types.
  • Functions: Can be generic to operate on different types.
  • Structs and Enums: Can be defined with generic type parameters.
  • Traits: Can be generic and implemented for various types.
  • Constraints: Trait bounds can be used to ensure generic types implement certain traits.
  • Lifetimes: Can be combined with generics to ensure references are valid and prevent dangling references.

Generics in Rust are a key feature for writing type-safe, reusable, and abstract code, enabling powerful abstractions while maintaining performance and safety.

Recent job openings