Los genéricos en Rust permiten escribir código flexible y reutilizable. En lugar de definir funciones, structs o enums para cada tipo de dato específico, puedes usar genéricos para trabajar con cualquier tipo de dato. Este enfoque no solo reduce la duplicación de código, sino que también mejora la legibilidad y el mantenimiento del código.

Conceptos Clave

  1. Definición de Genéricos: Los genéricos se definen utilizando el símbolo <T>, donde T es un marcador de posición para un tipo de dato.
  2. Funciones Genéricas: Permiten que las funciones trabajen con diferentes tipos de datos sin necesidad de definir múltiples versiones de la misma función.
  3. Structs Genéricos: Permiten que las estructuras almacenen diferentes tipos de datos.
  4. Enums Genéricos: Permiten que los enums manejen diferentes tipos de datos en sus variantes.

Funciones Genéricas

Ejemplo Básico

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

Explicación

  • fn largest<T: PartialOrd>(list: &[T]) -> &T: Define una función genérica largest que acepta una referencia a un slice de cualquier tipo T que implemente el trait PartialOrd (necesario para comparar elementos).
  • let mut largest = &list[0];: Inicializa largest con la referencia al primer elemento del slice.
  • for item in list: Itera sobre cada elemento en el slice.
  • if item > largest: Compara el elemento actual con el más grande encontrado hasta ahora.
  • largest = item;: Actualiza largest si se encuentra un elemento mayor.

Ejercicio

Escribe una función genérica smallest que encuentre el elemento más pequeño en un slice.

fn smallest<T: PartialOrd>(list: &[T]) -> &T {
    // Tu código aquí
}

Solución

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

Structs Genéricos

Ejemplo Básico

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

impl<T> Point<T> {
    fn new(x: T, y: T) -> Self {
        Point { x, y }
    }
}

Explicación

  • struct Point<T>: Define una estructura genérica Point con dos campos x y y del mismo tipo T.
  • impl<T> Point<T>: Implementa métodos para la estructura Point.
  • fn new(x: T, y: T) -> Self: Método constructor que crea una nueva instancia de Point.

Ejercicio

Define una estructura genérica Rectangle con campos width y height, y un método area que calcule el área del rectángulo.

struct Rectangle<T> {
    width: T,
    height: T,
}

impl<T> Rectangle<T> {
    fn area(&self) -> T {
        // Tu código aquí
    }
}

Solución

struct Rectangle<T> {
    width: T,
    height: T,
}

impl<T: std::ops::Mul<Output = T> + Copy> Rectangle<T> {
    fn area(&self) -> T {
        self.width * self.height
    }
}

Enums Genéricos

Ejemplo Básico

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

Explicación

  • enum Option<T>: Define un enum genérico Option con dos variantes: Some que contiene un valor de tipo T y None que no contiene ningún valor.

Ejercicio

Define un enum genérico Result con dos variantes: Ok que contiene un valor de tipo T y Err que contiene un valor de tipo E.

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

Solución

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

Conclusión

Los genéricos en Rust son una herramienta poderosa que permite escribir código más flexible y reutilizable. Al entender cómo definir y utilizar funciones, structs y enums genéricos, puedes crear programas más eficientes y fáciles de mantener. En el siguiente módulo, exploraremos la concurrencia en Rust, que te permitirá escribir programas que aprovechen al máximo los procesadores multinúcleo.

© Copyright 2024. Todos los derechos reservados